From 6a2cc37e68356bf28c677d6a57e9397fa5dd0e57 Mon Sep 17 00:00:00 2001 From: Steve Coffey Date: Fri, 13 Mar 2026 15:43:12 -0700 Subject: [PATCH 01/11] port sandbox runtime from universal_computer into the Agents SDK (#2) (#6) This pull request adds a native sandbox runtime to `openai-agents-python` by porting the relevant `universal_computer` sandbox pieces into `src/agents/sandbox` and reshaping them around the existing Agents SDK model instead of preserving the old UC agent stack. The main change is a new `SandboxAgent` + `SandboxAgentRunner` flow that fits the normal `Agent`/`Runner` paradigm while still supporting manifests, capabilities, session state, snapshots, and sandbox-backed tools. Along the way, this PR ports the sandbox sessions, manifests, entries, utilities, and Docker/Modal/E2B/Unix backends, removes the stale `universal_computer` import paths, and makes the sandbox code Python 3.10 compatible. This pull request also cleans up the port rather than carrying forward legacy UC shapes wholesale. In particular, it drops the old PTY/session-terminal surface and the legacy context-manager-oriented DX, uses concrete sandbox types instead of a protocol-heavy type layer, and keeps Docker as an optional extra rather than a core dependency. To make the new flow easier to try, it adds a Docker example that streams agent text and tool activity as it runs, plus focused sandbox tests covering manifests, snapshots, session behavior, and the runner integration. --------- --------- Co-authored-by: Kazuhiro Sera --- AGENTS.md | 1 + examples/sandbox/__init__.py | 1 + examples/sandbox/docker_runner.py | 142 + examples/sandbox/extensions/README.md | 114 + examples/sandbox/extensions/__init__.py | 1 + examples/sandbox/extensions/e2b_runner.py | 209 ++ examples/sandbox/extensions/modal_runner.py | 161 + examples/sandbox/handoffs.py | 107 + examples/sandbox/misc/__init__.py | 1 + examples/sandbox/misc/example_support.py | 33 + .../misc/reference_policy_mcp_server.py | 25 + examples/sandbox/misc/workspace_shell.py | 56 + examples/sandbox/sandbox_agent_with_tools.py | 121 + examples/sandbox/sandbox_agents_as_tools.py | 207 ++ examples/sandbox/unix_local_runner.py | 115 + pyproject.toml | 21 +- src/agents/_public_agent.py | 21 + src/agents/agent.py | 8 +- src/agents/extensions/sandbox/__init__.py | 49 + .../extensions/sandbox/sandboxes/__init__.py | 49 + .../extensions/sandbox/sandboxes/e2b.py | 968 ++++++ .../extensions/sandbox/sandboxes/modal.py | 1011 ++++++ src/agents/result.py | 132 +- src/agents/run.py | 150 +- src/agents/run_config.py | 32 + src/agents/run_internal/agent_bindings.py | 38 + .../run_internal/agent_runner_helpers.py | 10 +- src/agents/run_internal/run_loop.py | 155 +- src/agents/run_internal/tool_execution.py | 54 +- src/agents/run_internal/tool_planning.py | 24 +- src/agents/run_internal/tool_use_tracker.py | 25 +- src/agents/run_internal/turn_resolution.py | 120 +- src/agents/run_state.py | 687 +++- src/agents/sandbox/__init__.py | 46 + src/agents/sandbox/capabilities/__init__.py | 3 + src/agents/sandbox/capabilities/capability.py | 80 + src/agents/sandbox/entries/__init__.py | 34 + src/agents/sandbox/entries/artifacts.py | 326 ++ src/agents/sandbox/entries/base.py | 147 + src/agents/sandbox/entries/mounts/__init__.py | 23 + src/agents/sandbox/entries/mounts/base.py | 107 + src/agents/sandbox/entries/mounts/patterns.py | 748 +++++ .../sandbox/entries/mounts/providers.py | 320 ++ src/agents/sandbox/errors.py | 649 ++++ src/agents/sandbox/files.py | 26 + src/agents/sandbox/manifest.py | 142 + src/agents/sandbox/manifest_render.py | 163 + src/agents/sandbox/materialization.py | 68 + src/agents/sandbox/py.typed | 0 src/agents/sandbox/runtime.py | 125 + .../sandbox/runtime_agent_preparation.py | 97 + src/agents/sandbox/runtime_session_manager.py | 631 ++++ src/agents/sandbox/sandbox_agent.py | 28 + src/agents/sandbox/sandboxes/__init__.py | 41 + src/agents/sandbox/sandboxes/docker.py | 619 ++++ src/agents/sandbox/sandboxes/unix_local.py | 568 ++++ src/agents/sandbox/session/__init__.py | 118 + .../sandbox/session/archive_extraction.py | 299 ++ .../sandbox/session/base_sandbox_session.py | 442 +++ src/agents/sandbox/session/dependencies.py | 201 ++ src/agents/sandbox/session/events.py | 89 + src/agents/sandbox/session/manager.py | 159 + .../sandbox/session/manifest_application.py | 169 + src/agents/sandbox/session/sandbox_client.py | 86 + src/agents/sandbox/session/sandbox_session.py | 398 +++ .../sandbox/session/sandbox_session_state.py | 25 + src/agents/sandbox/session/sinks.py | 334 ++ src/agents/sandbox/session/utils.py | 37 + .../sandbox/session/workspace_payloads.py | 15 + src/agents/sandbox/snapshot.py | 138 + src/agents/sandbox/snapshot_defaults.py | 89 + src/agents/sandbox/types.py | 147 + src/agents/sandbox/util/__init__.py | 42 + src/agents/sandbox/util/checksums.py | 15 + src/agents/sandbox/util/deep_merge.py | 21 + src/agents/sandbox/util/github.py | 53 + src/agents/sandbox/util/iterator_io.py | 69 + src/agents/sandbox/util/parse_utils.py | 59 + src/agents/sandbox/util/retry.py | 127 + src/agents/sandbox/util/tar_utils.py | 150 + .../experiemental/codex/test_codex_tool.py | 3 +- tests/extensions/test_sandbox_e2b.py | 309 ++ tests/extensions/test_sandbox_modal.py | 215 ++ tests/test_agent_runner.py | 5 +- tests/test_agent_runner_sync.py | 12 +- tests/test_computer_action.py | 2 +- tests/test_example_workflows.py | 162 + tests/test_hitl_error_scenarios.py | 245 +- tests/test_run_impl_resume_paths.py | 120 +- tests/test_run_state.py | 399 ++- tests/test_run_step_execution.py | 221 +- tests/test_run_step_processing.py | 4 +- tests/test_sandbox_dependencies.py | 169 + tests/test_sandbox_docker.py | 585 ++++ tests/test_sandbox_entries.py | 126 + tests/test_sandbox_extract.py | 279 ++ tests/test_sandbox_manifest.py | 74 + tests/test_sandbox_manifest_application.py | 293 ++ tests/test_sandbox_mounts.py | 107 + tests/test_sandbox_retry.py | 165 + tests/test_sandbox_runtime.py | 2859 +++++++++++++++++ tests/test_sandbox_session_manager.py | 231 ++ tests/test_sandbox_session_sinks.py | 263 ++ tests/test_sandbox_session_utils.py | 165 + tests/test_sandbox_snapshot.py | 153 + tests/test_sandbox_snapshot_defaults.py | 105 + tests/test_server_conversation_tracker.py | 7 +- tests/test_shell_tool.py | 2 +- tests/test_tool_use_tracker.py | 53 + uv.lock | 378 ++- 110 files changed, 20510 insertions(+), 322 deletions(-) create mode 100644 examples/sandbox/__init__.py create mode 100644 examples/sandbox/docker_runner.py create mode 100644 examples/sandbox/extensions/README.md create mode 100644 examples/sandbox/extensions/__init__.py create mode 100644 examples/sandbox/extensions/e2b_runner.py create mode 100644 examples/sandbox/extensions/modal_runner.py create mode 100644 examples/sandbox/handoffs.py create mode 100644 examples/sandbox/misc/__init__.py create mode 100644 examples/sandbox/misc/example_support.py create mode 100644 examples/sandbox/misc/reference_policy_mcp_server.py create mode 100644 examples/sandbox/misc/workspace_shell.py create mode 100644 examples/sandbox/sandbox_agent_with_tools.py create mode 100644 examples/sandbox/sandbox_agents_as_tools.py create mode 100644 examples/sandbox/unix_local_runner.py create mode 100644 src/agents/_public_agent.py create mode 100644 src/agents/extensions/sandbox/__init__.py create mode 100644 src/agents/extensions/sandbox/sandboxes/__init__.py create mode 100644 src/agents/extensions/sandbox/sandboxes/e2b.py create mode 100644 src/agents/extensions/sandbox/sandboxes/modal.py create mode 100644 src/agents/run_internal/agent_bindings.py create mode 100644 src/agents/sandbox/__init__.py create mode 100644 src/agents/sandbox/capabilities/__init__.py create mode 100644 src/agents/sandbox/capabilities/capability.py create mode 100644 src/agents/sandbox/entries/__init__.py create mode 100644 src/agents/sandbox/entries/artifacts.py create mode 100644 src/agents/sandbox/entries/base.py create mode 100644 src/agents/sandbox/entries/mounts/__init__.py create mode 100644 src/agents/sandbox/entries/mounts/base.py create mode 100644 src/agents/sandbox/entries/mounts/patterns.py create mode 100644 src/agents/sandbox/entries/mounts/providers.py create mode 100644 src/agents/sandbox/errors.py create mode 100644 src/agents/sandbox/files.py create mode 100644 src/agents/sandbox/manifest.py create mode 100644 src/agents/sandbox/manifest_render.py create mode 100644 src/agents/sandbox/materialization.py create mode 100644 src/agents/sandbox/py.typed create mode 100644 src/agents/sandbox/runtime.py create mode 100644 src/agents/sandbox/runtime_agent_preparation.py create mode 100644 src/agents/sandbox/runtime_session_manager.py create mode 100644 src/agents/sandbox/sandbox_agent.py create mode 100644 src/agents/sandbox/sandboxes/__init__.py create mode 100644 src/agents/sandbox/sandboxes/docker.py create mode 100644 src/agents/sandbox/sandboxes/unix_local.py create mode 100644 src/agents/sandbox/session/__init__.py create mode 100644 src/agents/sandbox/session/archive_extraction.py create mode 100644 src/agents/sandbox/session/base_sandbox_session.py create mode 100644 src/agents/sandbox/session/dependencies.py create mode 100644 src/agents/sandbox/session/events.py create mode 100644 src/agents/sandbox/session/manager.py create mode 100644 src/agents/sandbox/session/manifest_application.py create mode 100644 src/agents/sandbox/session/sandbox_client.py create mode 100644 src/agents/sandbox/session/sandbox_session.py create mode 100644 src/agents/sandbox/session/sandbox_session_state.py create mode 100644 src/agents/sandbox/session/sinks.py create mode 100644 src/agents/sandbox/session/utils.py create mode 100644 src/agents/sandbox/session/workspace_payloads.py create mode 100644 src/agents/sandbox/snapshot.py create mode 100644 src/agents/sandbox/snapshot_defaults.py create mode 100644 src/agents/sandbox/types.py create mode 100644 src/agents/sandbox/util/__init__.py create mode 100644 src/agents/sandbox/util/checksums.py create mode 100644 src/agents/sandbox/util/deep_merge.py create mode 100644 src/agents/sandbox/util/github.py create mode 100644 src/agents/sandbox/util/iterator_io.py create mode 100644 src/agents/sandbox/util/parse_utils.py create mode 100644 src/agents/sandbox/util/retry.py create mode 100644 src/agents/sandbox/util/tar_utils.py create mode 100644 tests/extensions/test_sandbox_e2b.py create mode 100644 tests/extensions/test_sandbox_modal.py create mode 100644 tests/test_sandbox_dependencies.py create mode 100644 tests/test_sandbox_docker.py create mode 100644 tests/test_sandbox_entries.py create mode 100644 tests/test_sandbox_extract.py create mode 100644 tests/test_sandbox_manifest.py create mode 100644 tests/test_sandbox_manifest_application.py create mode 100644 tests/test_sandbox_mounts.py create mode 100644 tests/test_sandbox_retry.py create mode 100644 tests/test_sandbox_runtime.py create mode 100644 tests/test_sandbox_session_manager.py create mode 100644 tests/test_sandbox_session_sinks.py create mode 100644 tests/test_sandbox_session_utils.py create mode 100644 tests/test_sandbox_snapshot.py create mode 100644 tests/test_sandbox_snapshot_defaults.py diff --git a/AGENTS.md b/AGENTS.md index 8724609508..9122b6aab6 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -85,6 +85,7 @@ The OpenAI Agents Python repository provides the Python Agents SDK, examples, an - `src/agents/run_state.py` (RunState serialization/deserialization) - `src/agents/run_internal/session_persistence.py` (session save/rewind) - If the serialized RunState shape changes, update `CURRENT_SCHEMA_VERSION` in `src/agents/run_state.py` and the related serialization/deserialization logic. Keep released schema versions readable, and feel free to renumber or squash unreleased schema versions before release when those intermediate snapshots are intentionally unsupported. +- When bumping `CURRENT_SCHEMA_VERSION`, also add or update the matching entry in `SCHEMA_VERSION_SUMMARIES` in `src/agents/run_state.py` so every supported version keeps a short historical note describing what changed in that schema. ## Operation Guide diff --git a/examples/sandbox/__init__.py b/examples/sandbox/__init__.py new file mode 100644 index 0000000000..f34898d916 --- /dev/null +++ b/examples/sandbox/__init__.py @@ -0,0 +1 @@ +# Make the examples/sandbox directory a package for tooling consistency. diff --git a/examples/sandbox/docker_runner.py b/examples/sandbox/docker_runner.py new file mode 100644 index 0000000000..a5f34ccda7 --- /dev/null +++ b/examples/sandbox/docker_runner.py @@ -0,0 +1,142 @@ +""" +Start here if you are new to Docker-backed sandbox examples. + +This file keeps the flow explicit: + +1. Build a manifest for the files that should appear in the sandbox workspace. +2. Create a sandbox agent that can inspect that workspace through one shell tool. +3. Start a Docker-backed sandbox session, stream the run, and print what happens. +""" + +import argparse +import asyncio +import sys +from pathlib import Path + +from docker import from_env as docker_from_env # type: ignore[import-untyped] +from openai.types.responses import ResponseTextDeltaEvent + +from agents import ModelSettings, Runner +from agents.run import RunConfig +from agents.sandbox import SandboxAgent, SandboxRunConfig +from agents.sandbox.sandboxes.docker import DockerSandboxClient, DockerSandboxClientOptions + +if __package__ is None or __package__ == "": + sys.path.insert(0, str(Path(__file__).resolve().parents[2])) + +from examples.sandbox.misc.example_support import text_manifest +from examples.sandbox.misc.workspace_shell import WorkspaceShellCapability + +DEFAULT_QUESTION = "Summarize this sandbox project in 2 sentences." + + +def _stream_event_banner(event_name: str) -> str | None: + if event_name == "tool_called": + return "[tool call] shell" + if event_name == "tool_output": + return "[tool output] shell" + return None + + +async def main(model: str, question: str) -> None: + # A manifest is the starting file tree for the sandbox workspace. + # Each key is a path inside the workspace and each value is the file content. + # `text_manifest()` keeps small text examples readable by hiding the bytes boilerplate. + manifest = text_manifest( + { + "README.md": ( + "# Demo Project\n\n" + "This sandbox contains a tiny demo project for the sandbox runner.\n" + "The goal is to show how Runner can prepare a Docker-backed workspace.\n" + ), + "src/app.py": 'def greet(name: str) -> str:\n return f"Hello, {name}!"\n', + "docs/notes.md": ( + "# Notes\n\n" + "- The example is intentionally minimal.\n" + "- The model should inspect files through the shell tool.\n" + ), + } + ) + + agent = SandboxAgent( + name="Docker Sandbox Assistant", + model=model, + # `instructions` is the base agent instructions for this example's task. + instructions=( + "Answer questions about the sandbox workspace. Inspect the project before answering, " + "and keep the response concise." + ), + # `developer_instructions` is appended after that as additional deterministic instructions. + # Here, the tiny-workspace constraint is kept in `developer_instructions`. + developer_instructions=( + "Do not guess file names like package.json or pyproject.toml. " + "This demo intentionally contains a tiny workspace." + ), + # `default_manifest` tells the sandbox agent which workspace it should expect. + default_manifest=manifest, + # `WorkspaceShellCapability()` exposes one shell tool so the model can inspect files. + capabilities=[WorkspaceShellCapability()], + # `tool_choice="required"` makes the demo more deterministic by forcing the model + # to look at the workspace instead of answering from prior assumptions. + model_settings=ModelSettings(tool_choice="required"), + ) + + # The Docker client owns the container lifecycle for the sandbox session. + docker_client = DockerSandboxClient(docker_from_env()) + + # `create()` allocates a fresh sandbox session backed by a Docker container. + # We pass the same manifest here so the container knows which files to materialize. + session = await docker_client.create( + manifest=manifest, + options=DockerSandboxClientOptions(image="python:3.11-slim"), + ) + try: + # `async with session` keeps the example on the public session lifecycle API. + # `Runner` reuses the already-running session without starting it a second time. + async with session: + # `Runner.run_streamed()` drives the model and yields text and tool events in real time. + result = Runner.run_streamed( + agent, + question, + run_config=RunConfig(sandbox=SandboxRunConfig(session=session)), + ) + saw_text_delta = False + saw_any_text = False + + # The stream contains raw text deltas from the assistant plus structured tool events. + async for event in 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 not None: + if saw_text_delta: + print() + saw_text_delta = False + print(banner) + + if saw_text_delta: + print() + if not saw_any_text: + print(result.final_output) + finally: + # The client still owns deleting the underlying Docker container. + await docker_client.delete(session) + + +if __name__ == "__main__": + parser = argparse.ArgumentParser() + parser.add_argument("--model", default="gpt-5.4", help="Model name to use.") + parser.add_argument("--question", default=DEFAULT_QUESTION, help="Prompt to send to the agent.") + args = parser.parse_args() + asyncio.run(main(args.model, args.question)) diff --git a/examples/sandbox/extensions/README.md b/examples/sandbox/extensions/README.md new file mode 100644 index 0000000000..89acd87c27 --- /dev/null +++ b/examples/sandbox/extensions/README.md @@ -0,0 +1,114 @@ +# Cloud Sandbox Extension Examples + +These examples are for manual verification of the cloud sandbox backends that +live under `agents.extensions.sandbox`. + +They intentionally keep the flow simple: + +1. Build a tiny manifest in memory. +2. Create a `SandboxAgent` that inspects that workspace through one shell tool. +3. Run the agent against either E2B or Modal. + +Both examples require `OPENAI_API_KEY`, because they call the model through the +normal `Runner` path. + +## E2B + +### Setup + +Install the repo extra: + +```bash +uv sync --extra e2b +``` + +Create an E2B account, create an API key, and export it as `E2B_API_KEY`. +The official setup docs are: + +- +- + +Export the required environment variables: + +```bash +export OPENAI_API_KEY=... +export E2B_API_KEY=... +``` + +### Run + +```bash +uv run python examples/sandbox/extensions/e2b_runner.py --stream +``` + +Useful flags: + +- `--sandbox-type e2b_code_interpreter_async` +- `--template ` +- `--timeout 300` +- `--pause-on-exit` + +The example defaults to `e2b_code_interpreter_async`, which matches the async +Code Interpreter backend supported by this repo. + +## Modal + +### Setup + +Install the repo extra: + +```bash +uv sync --extra modal +``` + +Authenticate Modal with either CLI token setup or environment variables. The +official references are: + +- +- +- + +If you want to configure credentials directly from the CLI: + +```bash +uv run modal token set --token-id --token-secret +``` + +Or export environment variables for the current shell: + +```bash +export OPENAI_API_KEY=... +export MODAL_TOKEN_ID=... +export MODAL_TOKEN_SECRET=... +``` + +### Run + +```bash +uv run python examples/sandbox/extensions/modal_runner.py \ + --app-name openai-agents-python-sandbox-example \ + --stream +``` + +Useful flags: + +- `--workspace-persistence tar` +- `--workspace-persistence snapshot_filesystem` +- `--sandbox-create-timeout-s 60` + +`app_name` is required by `ModalSandboxClientOptions`, so the example makes it +an explicit CLI flag instead of hiding it. + +## What to expect + +Each script asks the model to inspect a small workspace and summarize it. A +successful run should: + +1. Start the chosen cloud sandbox backend. +2. Materialize the manifest into the sandbox workspace. +3. Call the shell tool at least once. +4. Print either streamed text or a final short answer about the workspace. + +These examples are not live-validated in CI because they depend on external +cloud credentials, but they are shaped so contributors can verify backend +behavior locally with one command per provider. diff --git a/examples/sandbox/extensions/__init__.py b/examples/sandbox/extensions/__init__.py new file mode 100644 index 0000000000..fb3e80a2d0 --- /dev/null +++ b/examples/sandbox/extensions/__init__.py @@ -0,0 +1 @@ +"""Manual validation examples for cloud sandbox extensions.""" diff --git a/examples/sandbox/extensions/e2b_runner.py b/examples/sandbox/extensions/e2b_runner.py new file mode 100644 index 0000000000..973511cb4f --- /dev/null +++ b/examples/sandbox/extensions/e2b_runner.py @@ -0,0 +1,209 @@ +""" +Minimal E2B-backed sandbox example for manual validation. + +This example is intentionally small: it creates a tiny workspace, lets the +agent inspect it through one shell tool, and prints a short answer. +""" + +import argparse +import asyncio +import os +import sys +from pathlib import Path +from typing import Literal + +from openai.types.responses import ResponseTextDeltaEvent + +from agents import ModelSettings, Runner +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[3])) + +from examples.sandbox.misc.example_support import text_manifest +from examples.sandbox.misc.workspace_shell import WorkspaceShellCapability + +try: + from agents.extensions.sandbox import ( + E2BSandboxClient, + E2BSandboxClientOptions, + E2BSandboxType, + ) +except Exception as exc: # pragma: no cover - import path depends on optional extras + raise SystemExit( + "E2B sandbox examples require the optional repo extra.\n" + "Install it with: uv sync --extra e2b" + ) from exc + + +DEFAULT_QUESTION = "Summarize this cloud sandbox workspace in 2 sentences." +DEFAULT_SANDBOX_TYPE = E2BSandboxType.E2B_ASYNC.value + + +def _build_manifest() -> Manifest: + return text_manifest( + { + "README.md": ( + "# Renewal Notes\n\n" + "This workspace contains a tiny account review packet for manual sandbox testing.\n" + ), + "customer.md": ( + "# Customer\n\n" + "- Name: Northwind Health.\n" + "- Renewal date: 2026-04-15.\n" + "- Risk: unresolved SSO setup.\n" + ), + "next_steps.md": ( + "# Next steps\n\n" + "1. Finish the SSO fix.\n" + "2. Confirm legal language before procurement review.\n" + ), + } + ) + + +def _require_env(name: str) -> None: + if os.environ.get(name): + return + raise SystemExit(f"{name} must be set before running this example.") + + +def _rewrite_template_resolution_error(exc: Exception) -> None: + message = str(exc) + marker = "error resolving template '" + if marker not in message: + return + template = message.split(marker, 1)[1].split("'", 1)[0] + raise SystemExit( + f"E2B could not resolve template `{template}`.\n" + "Pass `--template ` with a template that exists for this E2B account/team. " + "If you were relying on the example default, the SDK default template for this backend is " + "not available in your current E2B environment." + ) from exc + + +async def main( + *, + model: str, + question: str, + sandbox_type: Literal[ + "e2b_code_interpreter_async", + "e2b_code_interpreter", + "e2b_async", + "e2b", + ], + template: str | None, + timeout: int | None, + pause_on_exit: bool, + stream: bool, +) -> None: + _require_env("OPENAI_API_KEY") + _require_env("E2B_API_KEY") + + manifest = _build_manifest() + agent = SandboxAgent( + name="E2B Sandbox Assistant", + model=model, + # `instructions` is the base agent instructions for this sandbox task. + instructions=( + "Answer questions about the sandbox workspace. Inspect the files before answering " + "and keep the response concise." + ), + # `developer_instructions` is appended after that as additional deterministic instructions. + # Here, the grounding constraints are kept in `developer_instructions`. + developer_instructions=( + "Do not invent files or statuses that are not present in the workspace. Cite the " + "file names you inspected." + ), + default_manifest=manifest, + capabilities=[WorkspaceShellCapability()], + model_settings=ModelSettings(tool_choice="required"), + ) + + run_config = RunConfig( + sandbox=SandboxRunConfig( + client=E2BSandboxClient(), + options=E2BSandboxClientOptions( + sandbox_type=E2BSandboxType(sandbox_type), + template=template, + timeout=timeout, + pause_on_exit=pause_on_exit, + ), + ), + workflow_name="E2B sandbox example", + ) + + if not stream: + try: + result = await Runner.run(agent, question, run_config=run_config) + except Exception as exc: + _rewrite_template_resolution_error(exc) + raise + print(result.final_output) + return + + try: + stream_result = Runner.run_streamed(agent, question, run_config=run_config) + except Exception as exc: + _rewrite_template_resolution_error(exc) + raise + saw_text_delta = False + try: + 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) + except Exception as exc: + _rewrite_template_resolution_error(exc) + raise + + if saw_text_delta: + print() + + +if __name__ == "__main__": + parser = argparse.ArgumentParser() + parser.add_argument("--model", default="gpt-5.4", help="Model name to use.") + parser.add_argument("--question", default=DEFAULT_QUESTION, help="Prompt to send to the agent.") + parser.add_argument( + "--sandbox-type", + default=DEFAULT_SANDBOX_TYPE, + choices=[member.value for member in E2BSandboxType], + help=( + "E2B sandbox implementation to create. Defaults to the generic async sandbox because " + "some installed code-interpreter SDK versions still request the legacy " + "`code-interpreter-v1` template." + ), + ) + parser.add_argument("--template", default=None, help="Optional E2B template name.") + parser.add_argument( + "--timeout", + type=int, + default=300, + help="Optional E2B sandbox timeout in seconds.", + ) + parser.add_argument( + "--pause-on-exit", + action="store_true", + default=False, + help="Pause the sandbox on shutdown instead of killing it.", + ) + parser.add_argument("--stream", action="store_true", default=False, help="Stream the response.") + args = parser.parse_args() + + asyncio.run( + main( + model=args.model, + question=args.question, + sandbox_type=args.sandbox_type, + template=args.template, + timeout=args.timeout, + pause_on_exit=args.pause_on_exit, + stream=args.stream, + ) + ) diff --git a/examples/sandbox/extensions/modal_runner.py b/examples/sandbox/extensions/modal_runner.py new file mode 100644 index 0000000000..b9bec2cf42 --- /dev/null +++ b/examples/sandbox/extensions/modal_runner.py @@ -0,0 +1,161 @@ +""" +Minimal Modal-backed sandbox example for manual validation. + +This example mirrors the local and Docker sandbox demos, but it sends the +workspace to a Modal sandbox. +""" + +import argparse +import asyncio +import os +import sys +from pathlib import Path +from typing import Literal + +from openai.types.responses import ResponseTextDeltaEvent + +from agents import ModelSettings, Runner +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[3])) + +from examples.sandbox.misc.example_support import text_manifest +from examples.sandbox.misc.workspace_shell import WorkspaceShellCapability + +try: + from agents.extensions.sandbox import ModalSandboxClient, ModalSandboxClientOptions +except Exception as exc: # pragma: no cover - import path depends on optional extras + raise SystemExit( + "Modal sandbox examples require the optional repo extra.\n" + "Install it with: uv sync --extra modal" + ) from exc + + +DEFAULT_QUESTION = "Summarize this cloud sandbox workspace in 2 sentences." + + +def _build_manifest() -> Manifest: + return text_manifest( + { + "README.md": ( + "# Modal Demo Workspace\n\n" + "This workspace exists to validate the Modal sandbox backend manually.\n" + ), + "incident.md": ( + "# Incident\n\n" + "- Customer: Fabrikam Retail.\n" + "- Issue: delayed reporting rollout.\n" + "- Primary blocker: incomplete security questionnaire.\n" + ), + "plan.md": ( + "# Plan\n\n" + "1. Close the questionnaire.\n" + "2. Reconfirm the rollout date with the customer.\n" + ), + } + ) + + +def _require_env(name: str) -> None: + if os.environ.get(name): + return + raise SystemExit(f"{name} must be set before running this example.") + + +async def main( + *, + model: str, + question: str, + app_name: str, + workspace_persistence: Literal["tar", "snapshot_filesystem"], + sandbox_create_timeout_s: float | None, + stream: bool, +) -> None: + _require_env("OPENAI_API_KEY") + + manifest = _build_manifest() + agent = SandboxAgent( + name="Modal Sandbox Assistant", + model=model, + # `instructions` is the base agent instructions for this sandbox task. + instructions=( + "Answer questions about the sandbox workspace. Inspect the files before answering " + "and keep the response concise." + ), + # `developer_instructions` is appended after that as additional deterministic instructions. + # Here, the grounding constraints are kept in `developer_instructions`. + developer_instructions=( + "Do not invent files or statuses that are not present in the workspace. Cite the " + "file names you inspected." + ), + default_manifest=manifest, + capabilities=[WorkspaceShellCapability()], + model_settings=ModelSettings(tool_choice="required"), + ) + + run_config = RunConfig( + sandbox=SandboxRunConfig( + client=ModalSandboxClient(), + options=ModalSandboxClientOptions( + app_name=app_name, + workspace_persistence=workspace_persistence, + sandbox_create_timeout_s=sandbox_create_timeout_s, + ), + ), + workflow_name="Modal sandbox example", + ) + + if not stream: + result = await Runner.run(agent, question, run_config=run_config) + print(result.final_output) + return + + stream_result = Runner.run_streamed(agent, question, run_config=run_config) + saw_text_delta = 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) + + if saw_text_delta: + print() + + +if __name__ == "__main__": + parser = argparse.ArgumentParser() + parser.add_argument("--model", default="gpt-5.4", help="Model name to use.") + parser.add_argument("--question", default=DEFAULT_QUESTION, help="Prompt to send to the agent.") + parser.add_argument( + "--app-name", + default="openai-agents-python-sandbox-example", + help="Modal app name to create or reuse for the sandbox.", + ) + parser.add_argument( + "--workspace-persistence", + default="tar", + choices=["tar", "snapshot_filesystem"], + help="Workspace persistence mode for the Modal sandbox.", + ) + parser.add_argument( + "--sandbox-create-timeout-s", + type=float, + default=None, + help="Optional timeout for creating the Modal sandbox.", + ) + parser.add_argument("--stream", action="store_true", default=False, help="Stream the response.") + args = parser.parse_args() + + asyncio.run( + main( + model=args.model, + question=args.question, + app_name=args.app_name, + workspace_persistence=args.workspace_persistence, + sandbox_create_timeout_s=args.sandbox_create_timeout_s, + stream=args.stream, + ) + ) diff --git a/examples/sandbox/handoffs.py b/examples/sandbox/handoffs.py new file mode 100644 index 0000000000..b7f1dc8864 --- /dev/null +++ b/examples/sandbox/handoffs.py @@ -0,0 +1,107 @@ +""" +Show how a non-sandbox agent can hand work to a sandbox agent. + +The intake agent never sees a workspace directly. It hands document-heavy work +to a sandbox reviewer, and that reviewer then hands the synthesized result to a +plain account-facing writer. +""" + +import argparse +import asyncio +import sys +from pathlib import Path + +from agents import Agent, Runner +from agents.run import RunConfig +from agents.sandbox import SandboxAgent, SandboxRunConfig +from agents.sandbox.sandboxes.unix_local import UnixLocalSandboxClient + +if __package__ is None or __package__ == "": + sys.path.insert(0, str(Path(__file__).resolve().parents[2])) + +from examples.sandbox.misc.example_support import text_manifest +from examples.sandbox.misc.workspace_shell import WorkspaceShellCapability + +DEFAULT_QUESTION = ( + "Review the attached onboarding packet and draft a short internal note for the account " + "executive about what to confirm before kickoff." +) + + +async def main(model: str, question: str) -> None: + # The manifest becomes the workspace that only the sandbox reviewer can inspect. + manifest = text_manifest( + { + "customer_background.md": ( + "# Customer background\n\n" + "- Customer: Bluebird Logistics.\n" + "- Region: North America.\n" + "- New purchase: analytics workspace plus SSO.\n" + ), + "kickoff_checklist.md": ( + "# Kickoff checklist\n\n" + "- Security questionnaire is still in review.\n" + "- Two customer admins still need to complete access training.\n" + "- Target kickoff date is next Tuesday.\n" + ), + "implementation_scope.md": ( + "# Implementation scope\n\n" + "- The customer wants historical data migration for 5 years of records.\n" + "- Data engineering support is available only starting next month.\n" + ), + } + ) + + # This final agent does not inspect files. It only rewrites reviewed facts into a note. + account_manager = Agent( + name="Account Executive Assistant", + model=model, + instructions=( + "You write concise internal updates for account teams. Convert the sandbox review " + "into a short note with a headline, the top risks, and a recommended next step." + ), + ) + + # This sandbox agent can inspect the workspace, then hand its findings to the writer above. + sandbox_reviewer = SandboxAgent( + name="Onboarding Packet Reviewer", + model=model, + # `instructions` is the base agent instructions for the review-and-handoff task. + instructions=( + "You inspect onboarding documents in the sandbox, verify the facts, then hand off " + "to the account executive assistant to draft the final note." + ), + # `developer_instructions` is appended after that as additional deterministic instructions. + # Here, "do not answer directly" is kept in `developer_instructions`. + developer_instructions="Do not answer the user directly after reviewing the packet.", + default_manifest=manifest, + handoffs=[account_manager], + capabilities=[WorkspaceShellCapability()], + ) + + # The starting agent is a normal agent. It only decides when to hand off into the sandbox. + intake_agent = Agent( + name="Deal Desk Intake", + model=model, + instructions=( + "You triage internal requests. If a request depends on attached documents, hand off " + "to the onboarding packet reviewer immediately." + ), + handoffs=[sandbox_reviewer], + ) + + result = await Runner.run( + intake_agent, + question, + run_config=RunConfig(sandbox=SandboxRunConfig(client=UnixLocalSandboxClient())), + ) + print(result.final_output) + + +if __name__ == "__main__": + parser = argparse.ArgumentParser() + parser.add_argument("--model", default="gpt-5.4", help="Model name to use.") + parser.add_argument("--question", default=DEFAULT_QUESTION, help="Prompt to send to the agent.") + args = parser.parse_args() + + asyncio.run(main(args.model, args.question)) diff --git a/examples/sandbox/misc/__init__.py b/examples/sandbox/misc/__init__.py new file mode 100644 index 0000000000..8a5a5231df --- /dev/null +++ b/examples/sandbox/misc/__init__.py @@ -0,0 +1 @@ +# Shared support code for sandbox examples. diff --git a/examples/sandbox/misc/example_support.py b/examples/sandbox/misc/example_support.py new file mode 100644 index 0000000000..0f6a1bb04a --- /dev/null +++ b/examples/sandbox/misc/example_support.py @@ -0,0 +1,33 @@ +from __future__ import annotations + +from collections.abc import Mapping + +from agents.sandbox import Manifest +from agents.sandbox.entries import File + + +def text_manifest(files: Mapping[str, str]) -> Manifest: + """Build a manifest from in-memory UTF-8 text files.""" + + return Manifest( + entries={path: File(content=contents.encode("utf-8")) for path, contents in files.items()} + ) + + +def tool_call_name(raw_item: object) -> str: + """Return a readable name for a raw tool call item.""" + + if isinstance(raw_item, dict): + name = raw_item.get("name") + item_type = raw_item.get("type") + else: + name = getattr(raw_item, "name", None) + item_type = getattr(raw_item, "type", None) + + if isinstance(name, str) and name: + return name + if item_type == "shell_call": + return "shell" + if isinstance(item_type, str): + return item_type + return "" diff --git a/examples/sandbox/misc/reference_policy_mcp_server.py b/examples/sandbox/misc/reference_policy_mcp_server.py new file mode 100644 index 0000000000..0e6486d575 --- /dev/null +++ b/examples/sandbox/misc/reference_policy_mcp_server.py @@ -0,0 +1,25 @@ +from mcp.server.fastmcp import FastMCP + +mcp = FastMCP("Reference Policy Server") + + +@mcp.tool() +def get_policy_reference(topic: str) -> str: + """Return short internal policy guidance for a supported topic.""" + normalized = topic.strip().lower() + if "discount" in normalized: + return ( + "Discount policy: discounts from 11 to 15 percent require regional sales director " + "approval. Discounts above 15 percent require both finance and the regional sales " + "director." + ) + if "security" in normalized or "review" in normalized: + return ( + "Security review policy: any new data export workflow must finish security review " + "before kickoff or production access." + ) + return "No policy reference is available for that topic in this demo." + + +if __name__ == "__main__": + mcp.run() diff --git a/examples/sandbox/misc/workspace_shell.py b/examples/sandbox/misc/workspace_shell.py new file mode 100644 index 0000000000..766167a535 --- /dev/null +++ b/examples/sandbox/misc/workspace_shell.py @@ -0,0 +1,56 @@ +from __future__ import annotations + +from agents.sandbox import Capability, Manifest +from agents.sandbox.session.base_sandbox_session import BaseSandboxSession +from agents.tool import ( + ShellCallOutcome, + ShellCommandOutput, + ShellCommandRequest, + ShellResult, + ShellTool, + Tool, +) + + +class WorkspaceShellCapability(Capability): + """Expose one shell tool for inspecting the active sandbox workspace.""" + + def __init__(self) -> None: + super().__init__(type="workspace_shell") + self._session: BaseSandboxSession | None = None + + def bind(self, session: BaseSandboxSession) -> None: + self._session = session + + def tools(self) -> list[Tool]: + return [ShellTool(executor=self._execute_shell)] + + async def instructions(self, manifest: Manifest) -> str | None: + _ = manifest + return ( + "Use the `shell` tool to inspect the sandbox workspace before answering. " + "The workspace root is the current working directory, so prefer relative paths " + "with commands like `pwd`, `find .`, and `cat`. Only cite files you actually read." + ) + + async def _execute_shell(self, request: ShellCommandRequest) -> ShellResult: + if self._session is None: + raise RuntimeError("Workspace shell is not bound to a sandbox session.") + + timeout_s = ( + request.data.action.timeout_ms / 1000 + if request.data.action.timeout_ms is not None + else None + ) + outputs: list[ShellCommandOutput] = [] + for command in request.data.action.commands: + result = await self._session.exec(command, timeout=timeout_s, shell=True) + outputs.append( + ShellCommandOutput( + command=command, + stdout=result.stdout.decode("utf-8", errors="replace"), + stderr=result.stderr.decode("utf-8", errors="replace"), + outcome=ShellCallOutcome(type="exit", exit_code=result.exit_code), + ) + ) + return ShellResult(output=outputs) diff --git a/examples/sandbox/sandbox_agent_with_tools.py b/examples/sandbox/sandbox_agent_with_tools.py new file mode 100644 index 0000000000..2e7987f8d4 --- /dev/null +++ b/examples/sandbox/sandbox_agent_with_tools.py @@ -0,0 +1,121 @@ +""" +Show how a sandbox agent can combine three tool sources in one run. + +This example gives the model: + +1. A sandbox workspace to inspect with the shared shell capability. +2. A normal local function tool for approval routing. +3. A local stdio MCP server for reference policy lookups. +""" + +import argparse +import asyncio +import sys +from pathlib import Path + +from agents import Runner, function_tool +from agents.mcp import MCPServerStdio +from agents.run import RunConfig +from agents.sandbox import SandboxAgent, SandboxRunConfig +from agents.sandbox.sandboxes.unix_local import UnixLocalSandboxClient + +if __package__ is None or __package__ == "": + sys.path.insert(0, str(Path(__file__).resolve().parents[2])) + +from examples.sandbox.misc.example_support import text_manifest, tool_call_name +from examples.sandbox.misc.workspace_shell import WorkspaceShellCapability + +DEFAULT_QUESTION = ( + "Review this enterprise renewal request. Tell me who needs to approve the discount, " + "whether security review is still open, and the most important note for the account team. " + "Confirm the approval and security answers against the reference policy server before you respond." +) + + +@function_tool +def get_discount_approval_path(discount_percent: int) -> str: + """Return the approver required for a proposed discount percentage.""" + if discount_percent <= 10: + return "The account executive can approve discounts up to 10 percent." + if discount_percent <= 15: + return "The regional sales director must approve discounts from 11 to 15 percent." + return "Finance and the regional sales director must both approve discounts above 15 percent." + + +async def main(model: str, question: str) -> None: + # This manifest becomes the workspace that the sandbox agent can inspect. + manifest = text_manifest( + { + "renewal_request.md": ( + "# Renewal request\n\n" + "- Customer: Contoso Manufacturing.\n" + "- Requested discount: 14 percent.\n" + "- Renewal term: 12 months.\n" + "- Requested close date: March 28.\n" + ), + "account_notes.md": ( + "# Account notes\n\n" + "- The customer expanded usage in two plants this quarter.\n" + "- Security review for the new data export workflow was opened last week.\n" + "- Procurement wants a final approval map before they send the order form.\n" + ), + } + ) + + # The reference MCP server is another local process. The agent can call its tools alongside + # the sandbox shell tool and the normal Python function tool. + async with MCPServerStdio( + name="Reference Policy Server", + params={ + "command": sys.executable, + "args": [ + str(Path(__file__).resolve().parent / "misc" / "reference_policy_mcp_server.py") + ], + }, + ) as server: + agent = SandboxAgent( + name="Renewal Review Assistant", + model=model, + # `instructions` is the base agent instructions for the multi-tool review task. + instructions=( + "You review renewal requests. Inspect the packet, use " + "`get_discount_approval_path` for discount routing, and use the MCP reference " + "policy server when you need confirmation. Before you answer, you must call " + "`get_discount_approval_path` and at least one MCP policy tool." + ), + # `developer_instructions` is appended after that as additional deterministic + # instructions. Here, the concise-answer constraint is kept there. + developer_instructions=( + "Keep the answer concise and business-ready. Mention which policy topic you " + "confirmed through MCP." + ), + default_manifest=manifest, + tools=[get_discount_approval_path], + mcp_servers=[server], + capabilities=[WorkspaceShellCapability()], + ) + + result = await Runner.run( + agent, + question, + run_config=RunConfig(sandbox=SandboxRunConfig(client=UnixLocalSandboxClient())), + ) + tool_names: list[str] = [] + for item in result.new_items: + if getattr(item, "type", None) != "tool_call_item": + continue + name = tool_call_name(item.raw_item) + if name: + tool_names.append(name) + if tool_names: + print(f"[tools used] {', '.join(tool_names)}") + print(result.final_output) + + +if __name__ == "__main__": + parser = argparse.ArgumentParser() + parser.add_argument("--model", default="gpt-5.4", help="Model name to use.") + parser.add_argument("--question", default=DEFAULT_QUESTION, help="Prompt to send to the agent.") + args = parser.parse_args() + + asyncio.run(main(args.model, args.question)) diff --git a/examples/sandbox/sandbox_agents_as_tools.py b/examples/sandbox/sandbox_agents_as_tools.py new file mode 100644 index 0000000000..4946931e9a --- /dev/null +++ b/examples/sandbox/sandbox_agents_as_tools.py @@ -0,0 +1,207 @@ +""" +Show how sandbox agents can be exposed as tools to a normal orchestrator. + +Each sandbox reviewer gets its own isolated workspace. The outer orchestrator +does not inspect files directly. It calls the reviewers as tools and combines +their outputs with a normal Python function tool. +""" + +import argparse +import asyncio +import json +import sys +from pathlib import Path +from typing import Literal + +from pydantic import BaseModel, Field + +from agents import Agent, ModelSettings, Runner, function_tool +from agents.run import RunConfig +from agents.sandbox import SandboxAgent, SandboxRunConfig +from agents.sandbox.sandboxes.unix_local import UnixLocalSandboxClient + +if __package__ is None or __package__ == "": + sys.path.insert(0, str(Path(__file__).resolve().parents[2])) + +from examples.sandbox.misc.example_support import text_manifest, tool_call_name +from examples.sandbox.misc.workspace_shell import WorkspaceShellCapability + +DEFAULT_QUESTION = ( + "Review the Acme renewal materials and give me a short recommendation for the deal desk. " + "Include pricing risk, rollout risk, and the most important next step." +) + + +class PricingPacketReview(BaseModel): + requested_discount_percent: int = Field( + description="Exact requested discount percentage from pricing_summary.md." + ) + requested_term_months: int = Field( + description="Exact requested renewal term in months from pricing_summary.md." + ) + pricing_risk: Literal["low", "medium", "high"] + summary: str = Field(description="Short pricing risk summary grounded in the reviewed files.") + recommended_next_step: str = Field( + description="Most important commercial next step for the deal desk." + ) + evidence_files: list[str] = Field( + description="File names that support the review.", min_length=1 + ) + + +class RolloutRiskReview(BaseModel): + rollout_risk: Literal["low", "medium", "high"] + summary: str = Field(description="Short rollout risk summary grounded in the reviewed files.") + blockers: list[str] = Field(description="Concrete rollout blockers from the reviewed files.") + recommended_next_step: str = Field( + description="Most important delivery next step for the deal desk." + ) + evidence_files: list[str] = Field( + description="File names that support the review.", min_length=1 + ) + + +async def _structured_tool_output_extractor(result) -> str: + final_output = result.final_output + if isinstance(final_output, BaseModel): + return json.dumps(final_output.model_dump(mode="json"), sort_keys=True) + return str(final_output) + + +@function_tool +def get_discount_approval_rule(discount_percent: int) -> str: + """Return the internal approver required for a proposed discount.""" + if discount_percent <= 10: + return "Discounts up to 10 percent can be approved by the account executive." + if discount_percent <= 15: + return "Discounts from 11 to 15 percent require regional sales director approval." + return "Discounts above 15 percent require finance and regional sales director approval." + + +async def main(model: str, question: str) -> None: + # This manifest is visible only to the pricing reviewer. + pricing_manifest = text_manifest( + { + "pricing_summary.md": ( + "# Pricing summary\n\n" + "- Current annual contract: $220,000.\n" + "- Requested renewal term: 24 months.\n" + "- Requested discount: 15 percent.\n" + "- Account executive target discount band: 8 to 10 percent.\n" + ), + "commercial_notes.md": ( + "# Commercial notes\n\n" + "- The customer expanded from 120 to 170 paid seats in the last 6 months.\n" + "- Procurement asked for one final concession to close before quarter end.\n" + ), + } + ) + + # This separate manifest is visible only to the rollout reviewer. + rollout_manifest = text_manifest( + { + "rollout_plan.md": ( + "# Rollout plan\n\n" + "- Customer wants a 30-day rollout for three new regional teams.\n" + "- Regional admins have not completed training yet.\n" + "- SSO migration is scheduled for the second week of the rollout.\n" + ), + "support_history.md": ( + "# Support history\n\n" + "- Two high-priority onboarding tickets were closed in the last quarter.\n" + "- No open production incidents.\n" + "- Customer success manager asked for a phased launch if the contract closes.\n" + ), + } + ) + + pricing_agent = SandboxAgent( + name="Pricing Packet Reviewer", + model=model, + instructions=( + "You inspect renewal pricing documents and return a structured commercial review. " + "Inspect the files before answering and extract the exact requested discount percent " + "and renewal term from pricing_summary.md." + ), + developer_instructions=( + "Use the shell tool before answering. requested_discount_percent must match the exact " + "integer in pricing_summary.md. requested_term_months must match the exact renewal " + "term from pricing_summary.md. Do not introduce any facts, incidents, or numbers that " + "are not present in pricing_summary.md or commercial_notes.md. evidence_files must " + "list only files you actually inspected." + ), + default_manifest=pricing_manifest, + capabilities=[WorkspaceShellCapability()], + model_settings=ModelSettings(tool_choice="required"), + output_type=PricingPacketReview, + ) + rollout_agent = SandboxAgent( + name="Rollout Risk Reviewer", + model=model, + instructions=( + "You inspect rollout plans and return a structured delivery review. Inspect the files " + "before answering and keep the output tightly grounded in the rollout documents." + ), + developer_instructions=( + "Use the shell tool before answering. blockers must only contain issues that appear in " + "rollout_plan.md or support_history.md. Do not introduce any extra numbers, incidents, " + "or stakeholders beyond those files. evidence_files must list only files you actually " + "inspected." + ), + default_manifest=rollout_manifest, + capabilities=[WorkspaceShellCapability()], + model_settings=ModelSettings(tool_choice="required"), + output_type=RolloutRiskReview, + ) + + # Each sandbox-backed tool gets its own run configuration so the workspaces stay isolated. + pricing_run_config = RunConfig(sandbox=SandboxRunConfig(client=UnixLocalSandboxClient())) + rollout_run_config = RunConfig(sandbox=SandboxRunConfig(client=UnixLocalSandboxClient())) + + orchestrator = Agent( + name="Revenue Operations Coordinator", + model=model, + instructions=( + "You coordinate renewal reviews. Before answering, you must use all three tools: " + "`review_pricing_packet`, `review_rollout_risk`, and `get_discount_approval_rule`. " + "The review tools return JSON. Use the exact `requested_discount_percent` field from " + "`review_pricing_packet` when calling `get_discount_approval_rule`. In the final " + "recommendation, use only facts and numbers that appear in the tool outputs, and do " + "not add any extra incidents, price points, or contract terms." + ), + model_settings=ModelSettings(tool_choice="required"), + tools=[ + pricing_agent.as_tool( + tool_name="review_pricing_packet", + tool_description="Inspect the pricing packet and summarize commercial risk.", + custom_output_extractor=_structured_tool_output_extractor, + run_config=pricing_run_config, + ), + rollout_agent.as_tool( + tool_name="review_rollout_risk", + tool_description="Inspect the rollout packet and summarize implementation risk.", + custom_output_extractor=_structured_tool_output_extractor, + run_config=rollout_run_config, + ), + get_discount_approval_rule, + ], + ) + + result = await Runner.run(orchestrator, question) + tool_names = [ + tool_call_name(item.raw_item) + for item in result.new_items + if getattr(item, "type", None) == "tool_call_item" + ] + if tool_names: + print(f"[tools used] {', '.join(tool_names)}") + print(result.final_output) + + +if __name__ == "__main__": + parser = argparse.ArgumentParser() + parser.add_argument("--model", default="gpt-5.4", help="Model name to use.") + parser.add_argument("--question", default=DEFAULT_QUESTION, help="Prompt to send to the agent.") + args = parser.parse_args() + + asyncio.run(main(args.model, args.question)) diff --git a/examples/sandbox/unix_local_runner.py b/examples/sandbox/unix_local_runner.py new file mode 100644 index 0000000000..06c1937a3a --- /dev/null +++ b/examples/sandbox/unix_local_runner.py @@ -0,0 +1,115 @@ +""" +Start here if you want the simplest Unix-local sandbox example. + +This file mirrors the Docker example, but the sandbox runs as a temporary local +workspace on macOS or Linux instead of inside a Docker container. +""" + +import argparse +import asyncio +import sys +from pathlib import Path + +from openai.types.responses import ResponseTextDeltaEvent + +from agents import Runner +from agents.run import RunConfig +from agents.sandbox import SandboxAgent, SandboxRunConfig +from agents.sandbox.sandboxes.unix_local import UnixLocalSandboxClient + +if __package__ is None or __package__ == "": + sys.path.insert(0, str(Path(__file__).resolve().parents[2])) + +from examples.sandbox.misc.example_support import text_manifest +from examples.sandbox.misc.workspace_shell import WorkspaceShellCapability + +DEFAULT_QUESTION = ( + "Review this renewal packet. Summarize the customer's situation, the likely blockers, " + "and the next two actions an account team should take." +) + + +async def main(model: str, question: str, stream: bool) -> None: + # The manifest is the file tree that will be materialized into the sandbox workspace. + manifest = text_manifest( + { + "account_brief.md": ( + "# Northwind Health\n\n" + "- Segment: Mid-market healthcare analytics provider.\n" + "- Annual contract value: $148,000.\n" + "- Renewal date: 2026-04-15.\n" + "- Executive sponsor: Director of Data Operations.\n" + ), + "renewal_request.md": ( + "# Renewal request\n\n" + "Northwind requested a 12 percent discount in exchange for a two-year renewal. " + "They also want a 45-day implementation timeline for a new reporting workspace.\n" + ), + "usage_notes.md": ( + "# Usage notes\n\n" + "- Weekly active users increased 18 percent over the last quarter.\n" + "- API traffic is stable.\n" + "- The customer still has one unresolved SSO configuration issue from onboarding.\n" + ), + "implementation_risks.md": ( + "# Delivery risks\n\n" + "- Security questionnaire for the new reporting workspace is not complete.\n" + "- Customer procurement requires final legal language by April 1.\n" + ), + } + ) + + # The sandbox agent sees the manifest as its workspace and uses one shared shell tool + # to inspect the files before answering. + agent = SandboxAgent( + name="Renewal Packet Analyst", + model=model, + # `instructions` is the base agent instructions for the renewal review task. + instructions=( + "You review renewal packets for an account team. Inspect the packet before answering. " + "Keep the response concise, business-focused, and cite the file names that support " + "each conclusion." + ), + # `developer_instructions` is appended after that as additional deterministic instructions. + # Here, the grounding constraints live in `developer_instructions`. + developer_instructions=( + "If a conclusion depends on a file, mention that file by name. Do not invent numbers " + "or statuses that are not present in the workspace." + ), + default_manifest=manifest, + capabilities=[WorkspaceShellCapability()], + ) + + # With Unix-local sandboxes, the runner creates and cleans up the temporary workspace for us. + run_config = RunConfig( + sandbox=SandboxRunConfig(client=UnixLocalSandboxClient()), + workflow_name="Unix local sandbox review", + ) + + if not stream: + result = await Runner.run(agent, question, run_config=run_config) + print(result.final_output) + return + + # The streaming path prints text deltas as they arrive so the example behaves like a demo. + stream_result = Runner.run_streamed(agent, question, run_config=run_config) + saw_text_delta = 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) + + if saw_text_delta: + print() + + +if __name__ == "__main__": + parser = argparse.ArgumentParser() + parser.add_argument("--model", default="gpt-5.4", help="Model name to use.") + parser.add_argument("--question", default=DEFAULT_QUESTION, help="Prompt to send to the agent.") + parser.add_argument("--stream", action="store_true", default=False, help="Stream the response.") + args = parser.parse_args() + + asyncio.run(main(args.model, args.question, args.stream)) diff --git a/pyproject.toml b/pyproject.toml index f34a02e473..c82c57597c 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -42,6 +42,9 @@ sqlalchemy = ["SQLAlchemy>=2.0", "asyncpg>=0.29.0"] encrypt = ["cryptography>=45.0, <46"] redis = ["redis>=7"] dapr = ["dapr>=1.16.0", "grpcio>=1.60.0"] +docker = ["docker>=6.1"] +modal = ["modal>=1.3.1"] +e2b = ["e2b>=2.12.1", "e2b-code-interpreter>=1.0"] [dependency-groups] dev = [ @@ -51,7 +54,7 @@ dev = [ "pytest-asyncio", "pytest-mock>=3.14.0", "pytest-xdist", - "rich>=13.1.0, <14", + "rich>=13.1.0, <15", "mkdocs>=1.6.0", "mkdocs-material>=9.6.0", "mkdocstrings[python]>=0.28.0", @@ -125,7 +128,21 @@ ignore_missing_imports = true [tool.coverage.run] source = ["src/agents"] -omit = ["tests/*"] +omit = [ + "tests/*", + "src/agents/sandbox/sandboxes/*.py", + "src/agents/sandbox/task_context.py", + "src/agents/sandbox/task_runtime.py", + "src/agents/sandbox/materialization.py", + "src/agents/sandbox/entries/artifacts.py", + "src/agents/sandbox/entries/mounts/*.py", + "src/agents/sandbox/util/checksums.py", + "src/agents/sandbox/util/deep_merge.py", + "src/agents/sandbox/util/github.py", + "src/agents/sandbox/util/iterator_io.py", + "src/agents/sandbox/util/parse_utils.py", + "src/agents/sandbox/util/tar_utils.py", +] [tool.coverage.report] show_missing = true diff --git a/src/agents/_public_agent.py b/src/agents/_public_agent.py new file mode 100644 index 0000000000..e9550a31a2 --- /dev/null +++ b/src/agents/_public_agent.py @@ -0,0 +1,21 @@ +"""Helpers for preserving the user-visible agent identity during execution rewrites.""" + +from __future__ import annotations + +from .agent import Agent + +_PUBLIC_AGENT_ATTR = "_agents_public_agent" + + +def set_public_agent(execution_agent: Agent, public_agent: Agent) -> Agent: + """Tag an execution-only clone with the agent identity exposed to hooks and results.""" + setattr(execution_agent, _PUBLIC_AGENT_ATTR, public_agent) + return execution_agent + + +def get_public_agent(agent: Agent) -> Agent: + """Return the user-visible agent identity for hooks, tool execution, and results.""" + public_agent = getattr(agent, _PUBLIC_AGENT_ATTR, None) + if isinstance(public_agent, Agent): + return public_agent + return agent diff --git a/src/agents/agent.py b/src/agents/agent.py index dd291fcb8b..28999e6a0b 100644 --- a/src/agents/agent.py +++ b/src/agents/agent.py @@ -893,4 +893,10 @@ async def get_prompt( self, run_context: RunContextWrapper[TContext] ) -> ResponsePromptParam | None: """Get the prompt for the agent.""" - return await PromptUtil.to_model_input(self.prompt, run_context, self) + from ._public_agent import get_public_agent + + return await PromptUtil.to_model_input( + self.prompt, + run_context, + cast(Agent[TContext], get_public_agent(self)), + ) diff --git a/src/agents/extensions/sandbox/__init__.py b/src/agents/extensions/sandbox/__init__.py new file mode 100644 index 0000000000..3c0cb682d4 --- /dev/null +++ b/src/agents/extensions/sandbox/__init__.py @@ -0,0 +1,49 @@ +try: + from .sandboxes import ( + E2BSandboxClient as E2BSandboxClient, + E2BSandboxClientOptions as E2BSandboxClientOptions, + E2BSandboxSession as E2BSandboxSession, + E2BSandboxSessionState as E2BSandboxSessionState, + E2BSandboxTimeouts as E2BSandboxTimeouts, + E2BSandboxType as E2BSandboxType, + ) + + _HAS_E2B = True +except Exception: # pragma: no cover + _HAS_E2B = False + +try: + from .sandboxes import ( + ModalSandboxClient as ModalSandboxClient, + ModalSandboxClientOptions as ModalSandboxClientOptions, + ModalSandboxSession as ModalSandboxSession, + ModalSandboxSessionState as ModalSandboxSessionState, + ) + + _HAS_MODAL = True +except Exception: # pragma: no cover + _HAS_MODAL = False + +__all__: list[str] = [] + +if _HAS_E2B: + __all__.extend( + [ + "E2BSandboxClient", + "E2BSandboxClientOptions", + "E2BSandboxSession", + "E2BSandboxSessionState", + "E2BSandboxTimeouts", + "E2BSandboxType", + ] + ) + +if _HAS_MODAL: + __all__.extend( + [ + "ModalSandboxClient", + "ModalSandboxClientOptions", + "ModalSandboxSession", + "ModalSandboxSessionState", + ] + ) diff --git a/src/agents/extensions/sandbox/sandboxes/__init__.py b/src/agents/extensions/sandbox/sandboxes/__init__.py new file mode 100644 index 0000000000..95c089bdaf --- /dev/null +++ b/src/agents/extensions/sandbox/sandboxes/__init__.py @@ -0,0 +1,49 @@ +try: + from .e2b import ( + E2BSandboxClient as E2BSandboxClient, + E2BSandboxClientOptions as E2BSandboxClientOptions, + E2BSandboxSession as E2BSandboxSession, + E2BSandboxSessionState as E2BSandboxSessionState, + E2BSandboxTimeouts as E2BSandboxTimeouts, + E2BSandboxType as E2BSandboxType, + ) + + _HAS_E2B = True +except Exception: # pragma: no cover + _HAS_E2B = False + +try: + from .modal import ( + ModalSandboxClient as ModalSandboxClient, + ModalSandboxClientOptions as ModalSandboxClientOptions, + ModalSandboxSession as ModalSandboxSession, + ModalSandboxSessionState as ModalSandboxSessionState, + ) + + _HAS_MODAL = True +except Exception: # pragma: no cover + _HAS_MODAL = False + +__all__: list[str] = [] + +if _HAS_E2B: + __all__.extend( + [ + "E2BSandboxClient", + "E2BSandboxClientOptions", + "E2BSandboxSession", + "E2BSandboxSessionState", + "E2BSandboxTimeouts", + "E2BSandboxType", + ] + ) + +if _HAS_MODAL: + __all__.extend( + [ + "ModalSandboxClient", + "ModalSandboxClientOptions", + "ModalSandboxSession", + "ModalSandboxSessionState", + ] + ) diff --git a/src/agents/extensions/sandbox/sandboxes/e2b.py b/src/agents/extensions/sandbox/sandboxes/e2b.py new file mode 100644 index 0000000000..6e222bf070 --- /dev/null +++ b/src/agents/extensions/sandbox/sandboxes/e2b.py @@ -0,0 +1,968 @@ +from __future__ import annotations + +import base64 +import binascii +import inspect +import io +import shlex +import tarfile +import uuid +from collections.abc import Awaitable, Mapping +from dataclasses import dataclass +from enum import Enum +from pathlib import Path, PurePosixPath +from typing import cast + +from pydantic import BaseModel, Field + +from ....sandbox.entries import Mount, resolve_workspace_path +from ....sandbox.errors import ( + ExecNonZeroError, + ExecTimeoutError, + ExecTransportError, + WorkspaceArchiveReadError, + WorkspaceArchiveWriteError, + WorkspaceReadNotFoundError, + WorkspaceStartError, + WorkspaceWriteTypeError, +) +from ....sandbox.manifest import Manifest +from ....sandbox.session import SandboxSession, SandboxSessionState +from ....sandbox.session.base_sandbox_session import BaseSandboxSession +from ....sandbox.session.dependencies import Dependencies +from ....sandbox.session.manager import Instrumentation +from ....sandbox.session.sandbox_client import BaseSandboxClient +from ....sandbox.snapshot import SnapshotSpec, resolve_snapshot +from ....sandbox.types import ExecResult +from ....sandbox.util.retry import ( + TRANSIENT_HTTP_STATUS_CODES, + exception_chain_contains_type, + exception_chain_has_status_code, + retry_async, +) + + +class _E2BFilesAPI: + def write( + self, + path: str, + data: bytes, + request_timeout: float | None = None, + ) -> object: + raise NotImplementedError + + def remove(self, path: str, request_timeout: float | None = None) -> object: + raise NotImplementedError + + def read(self, path: str, format: str = "bytes") -> object: + raise NotImplementedError + + +class _E2BCommandsAPI: + def run( + self, + command: str, + timeout: float | None = None, + cwd: str | None = None, + envs: dict[str, str] | None = None, + user: str | None = None, + ) -> object: + raise NotImplementedError + + +class _E2BSandboxAPI: + sandbox_id: object + files: _E2BFilesAPI + commands: _E2BCommandsAPI + + def beta_pause(self) -> object: + raise NotImplementedError + + def kill(self) -> object: + raise NotImplementedError + + def is_running(self, request_timeout: float | None = None) -> object: + raise NotImplementedError + + +class _E2BSandboxFactoryAPI: + def create( + self, + *, + template: str | None = None, + timeout: int | None = None, + metadata: dict[str, str] | None = None, + envs: dict[str, str] | None = None, + secure: bool = True, + allow_internet_access: bool = True, + ) -> object: + raise NotImplementedError + + def _cls_connect( + self, + *, + sandbox_id: str, + timeout: int | None = None, + ) -> object: + raise NotImplementedError + + +# NOTE: We avoid importing `e2b_code_interpreter` or `e2b` at module import time so that users +# without the optional dependency can still import the sandbox package (they just can't use the +# E2B sandbox). + + +class E2BSandboxType(str, Enum): + """Supported E2B sandbox implementations.""" + + CODE_INTERPRETER_ASYNC = "e2b_code_interpreter_async" + CODE_INTERPRETER = "e2b_code_interpreter" + E2B_ASYNC = "e2b_async" + E2B = "e2b" + + +def _coerce_sandbox_type(value: E2BSandboxType | str | None) -> E2BSandboxType: + if value is None: + raise ValueError( + "E2BSandboxClientOptions.sandbox_type is required. " + "Use one of: e2b_code_interpreter_async, e2b_code_interpreter, e2b_async, e2b." + ) + if isinstance(value, E2BSandboxType): + return value + try: + return E2BSandboxType(value) + except ValueError as e: + raise ValueError( + "Invalid E2BSandboxClientOptions.sandbox_type. " + "Use one of: e2b_code_interpreter_async, e2b_code_interpreter, e2b_async, e2b." + ) from e + + +def _import_sandbox_class(sandbox_type: E2BSandboxType) -> _E2BSandboxFactoryAPI: + if sandbox_type in { + E2BSandboxType.CODE_INTERPRETER_ASYNC, + E2BSandboxType.CODE_INTERPRETER, + }: + module_name = "e2b_code_interpreter" + class_name = ( + "AsyncSandbox" if sandbox_type is E2BSandboxType.CODE_INTERPRETER_ASYNC else "Sandbox" + ) + missing_msg = ( + "E2BSandboxClient requires the optional `e2b-code-interpreter` dependency.\n" + "Install the E2B extra before using this sandbox backend." + ) + else: + module_name = "e2b" + class_name = "AsyncSandbox" if sandbox_type is E2BSandboxType.E2B_ASYNC else "Sandbox" + missing_msg = ( + "E2BSandboxClient requires the optional `e2b` dependency.\n" + "Install the E2B extra before using this sandbox backend." + ) + + try: + module = __import__(module_name, fromlist=[class_name]) + sandbox_cls = getattr(module, class_name) + except Exception as e: # pragma: no cover - exercised via unit tests with fakes + if module_name == "e2b": + try: + module = __import__("e2b.sandbox", fromlist=[class_name]) + sandbox_cls = getattr(module, class_name) + except Exception: + raise ImportError(missing_msg) from e + else: + raise ImportError(missing_msg) from e + + return cast(_E2BSandboxFactoryAPI, sandbox_cls) + + +def _as_sandbox_api(sandbox: object) -> _E2BSandboxAPI: + return cast(_E2BSandboxAPI, sandbox) + + +def _sandbox_id(sandbox: object) -> object: + return _as_sandbox_api(sandbox).sandbox_id + + +def _sandbox_write_file( + sandbox: object, + path: str, + data: bytes, + *, + request_timeout: float | None = None, +) -> object: + return _as_sandbox_api(sandbox).files.write( + path, + data, + request_timeout=request_timeout, + ) + + +def _sandbox_remove_file( + sandbox: object, + path: str, + *, + request_timeout: float | None = None, +) -> object: + return _as_sandbox_api(sandbox).files.remove(path, request_timeout=request_timeout) + + +def _sandbox_read_file(sandbox: object, path: str, *, format: str = "bytes") -> object: + return _as_sandbox_api(sandbox).files.read(path, format=format) + + +def _sandbox_run_command( + sandbox: object, + command: str, + *, + timeout: float | None = None, + cwd: str | None = None, + envs: dict[str, str] | None = None, + user: str | None = None, +) -> object: + return _as_sandbox_api(sandbox).commands.run( + command, + timeout=timeout, + cwd=cwd, + envs=envs, + user=user, + ) + + +def _sandbox_pause(sandbox: object) -> object: + return _as_sandbox_api(sandbox).beta_pause() + + +def _sandbox_kill(sandbox: object) -> object: + return _as_sandbox_api(sandbox).kill() + + +def _sandbox_is_running(sandbox: object, *, request_timeout: float | None = None) -> object: + return _as_sandbox_api(sandbox).is_running(request_timeout=request_timeout) + + +def _sandbox_create( + sandbox_class: _E2BSandboxFactoryAPI, + *, + template: str | None = None, + timeout: int | None = None, + metadata: dict[str, str] | None = None, + envs: dict[str, str] | None = None, + secure: bool = True, + allow_internet_access: bool = True, +) -> object: + return sandbox_class.create( + template=template, + timeout=timeout, + metadata=metadata, + envs=envs, + secure=secure, + allow_internet_access=allow_internet_access, + ) + + +def _sandbox_connect( + sandbox_class: _E2BSandboxFactoryAPI, + *, + sandbox_id: str, + timeout: int | None = None, +) -> object: + return sandbox_class._cls_connect(sandbox_id=sandbox_id, timeout=timeout) + + +async def _maybe_await(value: object) -> object: + if inspect.isawaitable(value): + return await cast(Awaitable[object], value) + return value + + +def _import_e2b_exceptions() -> Mapping[str, type[BaseException]]: + """Best-effort import of E2B exception classes for classification.""" + + try: + from e2b.exceptions import ( # type: ignore[import-untyped] + NotFoundException, + SandboxException, + TimeoutException, + ) + except Exception: # pragma: no cover - handled by fallbacks + return {} + + return { + "not_found": cast(type[BaseException], NotFoundException), + "sandbox": cast(type[BaseException], SandboxException), + "timeout": cast(type[BaseException], TimeoutException), + } + + +def _import_command_exit_exception() -> type[BaseException] | None: + try: + from e2b.sandbox.commands.command_handle import ( # type: ignore[import-untyped] + CommandExitException, + ) + except Exception: # pragma: no cover - handled by fallbacks + return None + return cast(type[BaseException], CommandExitException) + + +def _retryable_persist_workspace_error_types() -> tuple[type[BaseException], ...]: + excs = _import_e2b_exceptions() + retryable: list[type[BaseException]] = [] + timeout_exc = excs.get("timeout") + if timeout_exc is not None: + retryable.append(timeout_exc) + return tuple(retryable) + + +class E2BSandboxTimeouts(BaseModel): + """Timeout configuration for E2B operations.""" + + # E2B commands default to a 60s timeout when `timeout=None`. Sandbox semantics + # for `timeout=None` are "no timeout", so we pass a large sentinel value instead. + exec_timeout_unbounded_s: float = Field(default=24 * 60 * 60, ge=1) # 24 hours + + # Keepalive / is_running should be quick; if it does not return promptly, + # the sandbox is unhealthy. + keepalive_s: float = Field(default=5, ge=1) + + # best-effort cleanup (e.g., removing temp tar files) should not block shutdown for long. + cleanup_s: float = Field(default=30, ge=1) + + # fast, small ops like `mkdir -p` / `cat` / metadata-ish operations. + fast_op_s: float = Field(default=10, ge=1) + + # uploading tar contents can take longer than fast ops. + file_upload_s: float = Field(default=30, ge=1) + + # snapshot tar ops can be heavier on large workspaces. + snapshot_tar_s: float = Field(default=60, ge=1) + + +@dataclass(frozen=True) +class E2BSandboxClientOptions: + """Client options for the E2B sandbox.""" + + sandbox_type: E2BSandboxType | str + template: str | None = None + timeout: int | None = None + metadata: dict[str, str] | None = None + envs: dict[str, str] | None = None + secure: bool = True + allow_internet_access: bool = True + timeouts: E2BSandboxTimeouts | dict[str, object] | None = None + pause_on_exit: bool = False + + +class E2BSandboxSessionState(SandboxSessionState): + sandbox_id: str + sandbox_type: E2BSandboxType = Field(default=E2BSandboxType.CODE_INTERPRETER_ASYNC) + template: str | None = None + sandbox_timeout: int | None = None + metadata: dict[str, str] | None = None + base_envs: dict[str, str] = Field(default_factory=dict) + secure: bool = True + allow_internet_access: bool = True + timeouts: E2BSandboxTimeouts = Field(default_factory=E2BSandboxTimeouts) + pause_on_exit: bool = False + workspace_root_ready: bool = False + + +class E2BSandboxSession(BaseSandboxSession): + """E2B-backed sandbox session implementation.""" + + state: E2BSandboxSessionState + _sandbox: object + _skip_start: bool + _workspace_root_ready: bool + _resume_preserves_system_state: bool + + def __init__( + self, + *, + state: E2BSandboxSessionState, + sandbox: object, + ) -> None: + self.state = state + self._sandbox = sandbox + self._skip_start = False + self._workspace_root_ready = state.workspace_root_ready + self._resume_preserves_system_state = False + + @classmethod + def from_state( + cls, + state: E2BSandboxSessionState, + *, + sandbox: object, + ) -> E2BSandboxSession: + return cls(state=state, sandbox=sandbox) + + @property + def sandbox_id(self) -> str: + return self.state.sandbox_id + + async def _resolved_envs(self) -> dict[str, str]: + manifest_envs = await self.state.manifest.environment.resolve() + # Manifest envs take precedence over base envs supplied via client options. + return {**self.state.base_envs, **manifest_envs} + + def _coerce_exec_timeout(self, timeout_s: float | None) -> float: + if timeout_s is None: + return float(self.state.timeouts.exec_timeout_unbounded_s) + if timeout_s <= 0: + # Sandbox timeout cannot be <= 0; use 1s and rely on caller semantics. + return 1.0 + return float(timeout_s) + + async def _ensure_dir(self, path: Path, *, reason: str) -> None: + """E2B does not support `mkdir`; write a marker file to create directories.""" + if path == Path("/"): + return + marker_path = path / ".uc_dir" + try: + await _maybe_await( + _sandbox_write_file( + self._sandbox, + str(marker_path), + b"", + request_timeout=self.state.timeouts.file_upload_s, + ) + ) + except Exception as e: # pragma: no cover - exercised via unit tests with fakes + raise WorkspaceArchiveWriteError(path=path, context={"reason": reason}, cause=e) from e + finally: + try: + await _maybe_await( + _sandbox_remove_file( + self._sandbox, + str(marker_path), + request_timeout=self.state.timeouts.cleanup_s, + ) + ) + except Exception: + pass + + async def _ensure_workspace_root(self) -> None: + """Create the workspace root so file operations can materialize the manifest.""" + await self._ensure_dir(Path(self.state.manifest.root), reason="root_marker_failed") + + async def _prepare_workspace_root_for_exec(self) -> None: + """Create the workspace root through the command API before using it as `cwd`.""" + root = str(Path(self.state.manifest.root)) + envs = await self._resolved_envs() + result = await _maybe_await( + _sandbox_run_command( + self._sandbox, + f"mkdir -p -- {shlex.quote(root)}", + timeout=self.state.timeouts.fast_op_s, + cwd="/", + envs=envs, + ) + ) + exit_code = int(getattr(result, "exit_code", 0) or 0) + if exit_code != 0: + raise WorkspaceStartError( + path=Path(self.state.manifest.root), + context={ + "reason": "workspace_root_nonzero_exit", + "exit_code": exit_code, + "stderr": str(getattr(result, "stderr", "") or ""), + }, + ) + self._workspace_root_ready = True + self.state.workspace_root_ready = True + + def should_provision_manifest_accounts_on_resume(self) -> bool: + return not self._resume_preserves_system_state + + async def start(self) -> None: + if self._skip_start: + if not self._workspace_root_ready: + try: + await self._prepare_workspace_root_for_exec() + except WorkspaceStartError: + raise + except Exception as e: + raise WorkspaceStartError(path=Path(self.state.manifest.root), cause=e) from e + return + try: + # Ensure the workspace root exists before manifest materialization/hydration occurs. + await self._ensure_workspace_root() + await self._prepare_workspace_root_for_exec() + except WorkspaceStartError: + raise + except Exception as e: + raise WorkspaceStartError(path=Path(self.state.manifest.root), cause=e) from e + + await super().start() + + async def stop(self) -> None: + await super().stop() + + async def shutdown(self) -> None: + # Best-effort kill of the remote sandbox. + try: + if self.state.pause_on_exit: + await _maybe_await(_sandbox_pause(self._sandbox)) + else: + await _maybe_await(_sandbox_kill(self._sandbox)) + except Exception: + if self.state.pause_on_exit: + try: + await _maybe_await(_sandbox_kill(self._sandbox)) + except Exception: + pass + else: + pass + + async def _exec_internal( + self, + *command: str | Path, + timeout: float | None = None, + ) -> ExecResult: + command_list = [str(c) for c in command] + envs = await self._resolved_envs() + cwd = self.state.manifest.root if self._workspace_root_ready else None + user: str | None = None + if command_list and command_list[0] == "sudo" and len(command_list) >= 4: + # Handle the `sudo -u -- ...` prefix introduced by SandboxSession.exec. + if command_list[1] == "-u" and command_list[3] == "--": + user = command_list[2] + command_list = command_list[4:] + + cmd_str = shlex.join(command_list) + exec_timeout = self._coerce_exec_timeout(timeout) + + e2b_exc = _import_e2b_exceptions() + timeout_exc = e2b_exc.get("timeout") + command_exit_exc = _import_command_exit_exception() + + try: + result = await _maybe_await( + _sandbox_run_command( + self._sandbox, + cmd_str, + timeout=exec_timeout, + cwd=cwd, + envs=envs, + user=user, + ) + ) + return ExecResult( + stdout=str(getattr(result, "stdout", "") or "").encode("utf-8", errors="replace"), + stderr=str(getattr(result, "stderr", "") or "").encode("utf-8", errors="replace"), + exit_code=int(getattr(result, "exit_code", 0) or 0), + ) + except Exception as e: # pragma: no cover - exercised via unit tests with fakes + if timeout_exc is not None and isinstance(e, timeout_exc): + raise ExecTimeoutError(command=command, timeout_s=timeout, cause=e) from e + + if command_exit_exc is not None and isinstance(e, command_exit_exc): + exit_code = int(getattr(e, "exit_code", 1) or 1) + stdout = str(getattr(e, "stdout", "") or "") + stderr = str(getattr(e, "stderr", "") or "") + return ExecResult( + stdout=stdout.encode("utf-8", errors="replace"), + stderr=stderr.encode("utf-8", errors="replace"), + exit_code=exit_code, + ) + + raise ExecTransportError(command=command, cause=e) from e + + async def read(self, path: Path) -> io.IOBase: + workspace_path = resolve_workspace_path( + Path(self.state.manifest.root), + path, + allow_absolute_within_root=True, + ) + + e2b_exc = _import_e2b_exceptions() + not_found_exc = e2b_exc.get("not_found") + + try: + content = await _maybe_await( + _sandbox_read_file(self._sandbox, str(workspace_path), format="bytes") + ) + if isinstance(content, (bytes, bytearray)): + data = bytes(content) + elif isinstance(content, str): + data = content.encode("utf-8", errors="replace") + else: + data = str(content).encode("utf-8", errors="replace") + return io.BytesIO(data) + except Exception as e: # pragma: no cover - exercised via unit tests with fakes + if not_found_exc is not None and isinstance(e, not_found_exc): + raise WorkspaceReadNotFoundError(path=path, cause=e) from e + raise WorkspaceArchiveReadError(path=path, cause=e) from e + + async def write(self, path: Path, data: io.IOBase) -> None: + payload = data.read() + if isinstance(payload, str): + payload = payload.encode("utf-8") + if not isinstance(payload, (bytes, bytearray)): + raise WorkspaceWriteTypeError(path=path, actual_type=type(payload).__name__) + + workspace_path = resolve_workspace_path( + Path(self.state.manifest.root), + path, + allow_absolute_within_root=True, + ) + + try: + await _maybe_await( + _sandbox_write_file( + self._sandbox, + str(workspace_path), + bytes(payload), + request_timeout=self.state.timeouts.file_upload_s, + ) + ) + except Exception as e: # pragma: no cover - exercised via unit tests with fakes + raise WorkspaceArchiveWriteError(path=workspace_path, cause=e) from e + + async def running(self) -> bool: + if not self._workspace_root_ready: + return False + try: + return bool( + await _maybe_await( + _sandbox_is_running( + self._sandbox, + request_timeout=self.state.timeouts.keepalive_s, + ) + ) + ) + except Exception: + return False + + async def mkdir(self, path: Path | str, *, parents: bool = False) -> None: + path = self.normalize_path(path) + if not parents: + parent = path.parent + test = await self.exec("test", "-d", str(parent), shell=False) + if not test.ok(): + raise ExecNonZeroError(test, command=("test", "-d", str(parent))) + await self._ensure_dir(path, reason="mkdir_failed") + + def _tar_exclude_args(self) -> list[str]: + excludes: list[str] = [] + for rel in sorted(self.state.manifest.ephemeral_entry_paths(), key=lambda p: p.as_posix()): + rel_posix = rel.as_posix().lstrip("/") + if not rel_posix or rel_posix in {".", "/"}: + continue + excludes.append(f"--exclude={shlex.quote(rel_posix)}") + excludes.append(f"--exclude={shlex.quote(f'./{rel_posix}')}") + return excludes + + @retry_async( + retry_if=lambda exc, self, tar_cmd: exception_chain_contains_type( + exc, _retryable_persist_workspace_error_types() + ) + or exception_chain_has_status_code(exc, TRANSIENT_HTTP_STATUS_CODES) + ) + async def _run_persist_workspace_command(self, tar_cmd: str) -> str: + try: + envs = await self._resolved_envs() + result = await _maybe_await( + _sandbox_run_command( + self._sandbox, + tar_cmd, + timeout=self.state.timeouts.snapshot_tar_s, + cwd="/", + envs=envs, + ) + ) + exit_code = int(getattr(result, "exit_code", 0) or 0) + if exit_code != 0: + raise WorkspaceArchiveReadError( + path=Path(self.state.manifest.root), + context={ + "reason": "snapshot_nonzero_exit", + "exit_code": exit_code, + "stderr": str(getattr(result, "stderr", "") or ""), + }, + ) + return str(getattr(result, "stdout", "") or "") + except WorkspaceArchiveReadError: + raise + except Exception as e: # pragma: no cover - exercised via unit tests with fakes + raise WorkspaceArchiveReadError(path=Path(self.state.manifest.root), cause=e) from e + + async def persist_workspace(self) -> io.IOBase: + root = Path(self.state.manifest.root) + excludes = " ".join(self._tar_exclude_args()) + tar_cmd = f"tar {excludes} -C {shlex.quote(str(root))} -cf - . | base64 -w0" + mounts: list[tuple[Mount, Path]] = [] + for dest, entry in self.state.manifest.entries.items(): + if isinstance(entry, Mount): + dest_path = dest if isinstance(dest, Path) else Path(dest) + mount_path = entry._resolve_mount_path(self, dest_path) + mounts.append((entry, mount_path)) + unmounted_mounts: list[tuple[Mount, Path]] = [] + for mount_entry, mount_path in mounts: + try: + await mount_entry.unmount_path(self, mount_path) + except Exception as e: + raise WorkspaceArchiveReadError(path=root, cause=e) from e + unmounted_mounts.append((mount_entry, mount_path)) + + snapshot_error: WorkspaceArchiveReadError | None = None + raw: bytes | None = None + try: + encoded = await self._run_persist_workspace_command(tar_cmd) + try: + raw = base64.b64decode(encoded.encode("utf-8"), validate=True) + except (binascii.Error, ValueError) as e: + raise WorkspaceArchiveReadError( + path=root, + context={"reason": "snapshot_invalid_base64"}, + cause=e, + ) from e + except WorkspaceArchiveReadError as e: + snapshot_error = e + + remount_error: WorkspaceArchiveReadError | None = None + for mount_entry, mount_path in reversed(unmounted_mounts): + try: + await mount_entry.mount(self, mount_path) + except Exception as e: + remount_error = WorkspaceArchiveReadError(path=root, cause=e) + break + + if snapshot_error is not None: + raise snapshot_error + if remount_error is not None: + raise remount_error + + assert raw is not None + return io.BytesIO(raw) + + def _validate_tar_bytes(self, raw: bytes) -> None: + try: + with tarfile.open(fileobj=io.BytesIO(raw), mode="r:*") as tar: + for member in tar.getmembers(): + name = member.name + if name in ("", ".", "./"): + continue + rel = PurePosixPath(name) + if rel.is_absolute(): + raise ValueError(f"absolute path member: {name}") + if ".." in rel.parts: + raise ValueError(f"parent traversal member: {name}") + if member.issym() or member.islnk(): + raise ValueError(f"link member not allowed: {name}") + if not (member.isdir() or member.isreg()): + raise ValueError(f"unsupported member type: {name}") + except (tarfile.TarError, OSError) as e: + raise ValueError("invalid tar stream") from e + + async def hydrate_workspace(self, data: io.IOBase) -> None: + root = Path(self.state.manifest.root) + tar_path = f"/tmp/uc-hydrate-{self.state.session_id.hex}.tar" + + raw = data.read() + if isinstance(raw, str): + raw = raw.encode("utf-8") + if not isinstance(raw, (bytes, bytearray)): + raise WorkspaceWriteTypeError(path=Path(tar_path), actual_type=type(raw).__name__) + + try: + self._validate_tar_bytes(bytes(raw)) + except ValueError as e: + raise WorkspaceArchiveWriteError( + path=root, + context={"reason": "unsafe_or_invalid_tar", "detail": str(e)}, + cause=e, + ) from e + + try: + await self._ensure_workspace_root() + envs = await self._resolved_envs() + await _maybe_await( + _sandbox_write_file( + self._sandbox, + tar_path, + bytes(raw), + request_timeout=self.state.timeouts.file_upload_s, + ) + ) + result = await _maybe_await( + _sandbox_run_command( + self._sandbox, + f"tar -C {shlex.quote(str(root))} -xf {shlex.quote(tar_path)}", + timeout=self.state.timeouts.snapshot_tar_s, + cwd="/", + envs=envs, + ) + ) + exit_code = int(getattr(result, "exit_code", 0) or 0) + if exit_code != 0: + raise WorkspaceArchiveWriteError( + path=root, + context={ + "reason": "hydrate_nonzero_exit", + "exit_code": exit_code, + "stderr": str(getattr(result, "stderr", "") or ""), + }, + ) + self._workspace_root_ready = True + self.state.workspace_root_ready = True + except WorkspaceArchiveWriteError: + raise + except Exception as e: # pragma: no cover - exercised via unit tests with fakes + raise WorkspaceArchiveWriteError(path=root, cause=e) from e + finally: + try: + envs = await self._resolved_envs() + await _maybe_await( + _sandbox_run_command( + self._sandbox, + f"rm -f -- {shlex.quote(tar_path)}", + timeout=self.state.timeouts.cleanup_s, + cwd="/", + envs=envs, + ) + ) + except Exception: + pass + + +class E2BSandboxClient(BaseSandboxClient[E2BSandboxClientOptions]): + backend_id = "e2b" + _instrumentation: Instrumentation + + def __init__( + self, + *, + instrumentation: Instrumentation | None = None, + dependencies: Dependencies | None = None, + ) -> None: + self._instrumentation = instrumentation or Instrumentation() + self._dependencies = dependencies + + async def create( + self, + *, + snapshot: SnapshotSpec | None = None, + manifest: Manifest | None = None, + options: E2BSandboxClientOptions, + ) -> SandboxSession: + if options is None: + raise ValueError("E2BSandboxClient.create requires options") + + manifest = manifest or Manifest() + sandbox_type = _coerce_sandbox_type(options.sandbox_type) + + timeouts_in = options.timeouts + if isinstance(timeouts_in, E2BSandboxTimeouts): + timeouts = timeouts_in + elif timeouts_in is None: + timeouts = E2BSandboxTimeouts() + else: + timeouts = E2BSandboxTimeouts.model_validate(timeouts_in) + + base_envs = dict(options.envs or {}) + manifest_envs = await manifest.environment.resolve() + envs = {**base_envs, **manifest_envs} or None + + SandboxClass = _import_sandbox_class(sandbox_type) + sandbox = await _maybe_await( + _sandbox_create( + SandboxClass, + template=options.template, + timeout=options.timeout, + metadata=options.metadata, + envs=envs, + secure=options.secure, + allow_internet_access=options.allow_internet_access, + ) + ) + + session_id = uuid.uuid4() + snapshot_instance = resolve_snapshot(snapshot, str(session_id)) + state = E2BSandboxSessionState( + session_id=session_id, + manifest=manifest, + snapshot=snapshot_instance, + sandbox_id=str(_sandbox_id(sandbox)), + sandbox_type=sandbox_type, + template=options.template, + sandbox_timeout=options.timeout, + metadata=options.metadata, + base_envs=base_envs, + secure=options.secure, + allow_internet_access=options.allow_internet_access, + timeouts=timeouts, + pause_on_exit=options.pause_on_exit, + ) + inner = E2BSandboxSession.from_state(state, sandbox=sandbox) + return self._wrap_session(inner, instrumentation=self._instrumentation) + + async def delete(self, session: SandboxSession) -> SandboxSession: + inner = session._inner + if not isinstance(inner, E2BSandboxSession): + raise TypeError("E2BSandboxClient.delete expects an E2BSandboxSession") + return session + + async def resume(self, state: SandboxSessionState) -> SandboxSession: + if not isinstance(state, E2BSandboxSessionState): + raise TypeError("E2BSandboxClient.resume expects an E2BSandboxSessionState") + + sandbox_type = _coerce_sandbox_type(state.sandbox_type) + SandboxClass = _import_sandbox_class(sandbox_type) + + base_envs = dict(state.base_envs) + manifest_envs = await state.manifest.environment.resolve() + envs = {**base_envs, **manifest_envs} or None + + sandbox: object + reconnected = False + try: + # `_cls_connect` is the current async entrypoint for re-attaching to a sandbox id. + sandbox = await _maybe_await( + _sandbox_connect( + SandboxClass, + sandbox_id=state.sandbox_id, + timeout=state.sandbox_timeout, + ) + ) + if not state.pause_on_exit: + is_running = await _maybe_await( + _sandbox_is_running(sandbox, request_timeout=state.timeouts.keepalive_s) + ) + if not is_running: + raise RuntimeError("sandbox_not_running") + reconnected = True + except Exception: + sandbox = await _maybe_await( + _sandbox_create( + SandboxClass, + template=state.template, + timeout=state.sandbox_timeout, + metadata=state.metadata, + envs=envs, + secure=state.secure, + allow_internet_access=state.allow_internet_access, + ) + ) + state.sandbox_id = str(_sandbox_id(sandbox)) + + inner = E2BSandboxSession.from_state(state, sandbox=sandbox) + inner._resume_preserves_system_state = reconnected + if state.pause_on_exit and reconnected: + inner._skip_start = True + else: + inner._skip_start = False + return self._wrap_session(inner, instrumentation=self._instrumentation) + + def deserialize_session_state(self, payload: dict[str, object]) -> SandboxSessionState: + return E2BSandboxSessionState.model_validate(payload) + + +__all__ = [ + "E2BSandboxClient", + "E2BSandboxClientOptions", + "E2BSandboxSession", + "E2BSandboxSessionState", + "E2BSandboxTimeouts", + "E2BSandboxType", +] diff --git a/src/agents/extensions/sandbox/sandboxes/modal.py b/src/agents/extensions/sandbox/sandboxes/modal.py new file mode 100644 index 0000000000..7f01187760 --- /dev/null +++ b/src/agents/extensions/sandbox/sandboxes/modal.py @@ -0,0 +1,1011 @@ +""" +Modal sandbox (https://modal.com) implementation. + +Run `python -m modal setup` to configure Modal locally. + +This module provides a Modal-backed sandbox client/session implementation backed by +`modal.Sandbox`. + +Note: The `modal` dependency is intended to be optional (installed via an extra), +so package-level exports should guard imports of this module. Within this module, +we import Modal normally so IDEs can resolve and navigate Modal types. +""" + +from __future__ import annotations + +import asyncio +import functools +import io +import json +import logging +import math +import shlex +import tarfile +import uuid +from collections.abc import Awaitable +from dataclasses import dataclass +from pathlib import Path +from typing import Callable, Literal, TypeVar, cast + +import modal +from modal.container_process import ContainerProcess + +from ....sandbox.entries import resolve_workspace_path +from ....sandbox.errors import ( + ExecTimeoutError, + ExecTransportError, + WorkspaceArchiveReadError, + WorkspaceArchiveWriteError, + WorkspaceReadNotFoundError, + WorkspaceStartError, + WorkspaceStopError, + WorkspaceWriteTypeError, +) +from ....sandbox.manifest import Manifest +from ....sandbox.session import SandboxSession, SandboxSessionState +from ....sandbox.session.base_sandbox_session import BaseSandboxSession +from ....sandbox.session.dependencies import Dependencies +from ....sandbox.session.manager import Instrumentation +from ....sandbox.session.sandbox_client import BaseSandboxClient +from ....sandbox.snapshot import SnapshotSpec, resolve_snapshot +from ....sandbox.types import ExecResult +from ....sandbox.util.retry import ( + TRANSIENT_HTTP_STATUS_CODES, + exception_chain_contains_type, + exception_chain_has_status_code, + retry_async, +) +from ....sandbox.util.tar_utils import UnsafeTarMemberError, should_skip_tar_member + +_DEFAULT_TIMEOUT_S = 30.0 +_DEFAULT_IMAGE_TAG = "python:3.11-slim" +_DEFAULT_SNAPSHOT_FILESYSTEM_TIMEOUT_S = 60.0 + +WorkspacePersistenceMode = Literal["tar", "snapshot_filesystem"] + +_WORKSPACE_PERSISTENCE_TAR: WorkspacePersistenceMode = "tar" +_WORKSPACE_PERSISTENCE_SNAPSHOT_FILESYSTEM: WorkspacePersistenceMode = "snapshot_filesystem" + +# Magic prefix for snapshot_filesystem payloads that cannot be represented as tar bytes. +_UC_MODAL_SNAPSHOT_FS_MAGIC = b"UC_MODAL_SNAPSHOT_FS_V1\n" + +logger = logging.getLogger(__name__) +R = TypeVar("R") + + +@dataclass(frozen=True) +class ModalSandboxClientOptions: + app_name: str + sandbox_create_timeout_s: float | None = None + workspace_persistence: WorkspacePersistenceMode = _WORKSPACE_PERSISTENCE_TAR + snapshot_filesystem_timeout_s: float | None = None + snapshot_filesystem_restore_timeout_s: float | None = None + + +def _encode_snapshot_filesystem_ref(*, snapshot_id: str) -> bytes: + # Small JSON envelope so we can round-trip a non-tar snapshot reference + # through Snapshot.persist(). + body = json.dumps({"snapshot_id": snapshot_id}, separators=(",", ":"), sort_keys=True).encode( + "utf-8" + ) + return _UC_MODAL_SNAPSHOT_FS_MAGIC + body + + +def _decode_snapshot_filesystem_ref(raw: bytes) -> str | None: + if not raw.startswith(_UC_MODAL_SNAPSHOT_FS_MAGIC): + return None + body = raw[len(_UC_MODAL_SNAPSHOT_FS_MAGIC) :] + try: + obj = json.loads(body.decode("utf-8")) + except Exception: + return None + snapshot_id = obj.get("snapshot_id") + return snapshot_id if isinstance(snapshot_id, str) and snapshot_id else None + + +@dataclass(frozen=True) +class ModalImageSelector: + """ + A single "image selector" type to avoid juggling image/image_id/image_tag separately. + """ + + kind: Literal["image", "id", "tag"] + value: modal.Image | str + + @classmethod + def from_image(cls, image: modal.Image) -> ModalImageSelector: + return cls(kind="image", value=image) + + @classmethod + def from_id(cls, image_id: str) -> ModalImageSelector: + return cls(kind="id", value=image_id) + + @classmethod + def from_tag(cls, image_tag: str) -> ModalImageSelector: + return cls(kind="tag", value=image_tag) + + +@dataclass(frozen=True) +class ModalSandboxSelector: + """ + A single "sandbox selector" type to avoid juggling sandbox/sandbox_id separately. + """ + + kind: Literal["sandbox", "id"] + value: modal.Sandbox | str + + @classmethod + def from_sandbox(cls, sandbox: modal.Sandbox) -> ModalSandboxSelector: + return cls(kind="sandbox", value=sandbox) + + @classmethod + def from_id(cls, sandbox_id: str) -> ModalSandboxSelector: + return cls(kind="id", value=sandbox_id) + + +class ModalSandboxSessionState(SandboxSessionState): + """ + Serializable state for a Modal-backed session. + + We store only values that can be safely persisted and later used by `resume()`. + """ + + app_name: str + # Optional Modal image object id (enables reconstructing a custom image via Image.from_id()). + image_id: str | None = None + # Registry image tag (e.g. "debian:bookworm" or "ghcr.io/org/img:tag"). + # Used when `image_id` isn't available and no in-memory image override was provided. + image_tag: str | None = None + # Timeout for creating a sandbox (Modal calls are synchronous from the user's perspective + # and can block; we wrap them in a thread with asyncio timeout). + sandbox_create_timeout_s: float = _DEFAULT_TIMEOUT_S + sandbox_id: str | None = None + # Workspace persistence mode: + # - "tar": create a tar stream in the sandbox via `tar cf - ...` and pull bytes back via stdout. + # - "snapshot_filesystem": use Modal's `Sandbox.snapshot_filesystem()` + # (if available) and persist a snapshot reference. + workspace_persistence: WorkspacePersistenceMode = _WORKSPACE_PERSISTENCE_TAR + # Async timeouts for snapshot_filesystem-based persistence and restore. + snapshot_filesystem_timeout_s: float = _DEFAULT_SNAPSHOT_FILESYSTEM_TIMEOUT_S + snapshot_filesystem_restore_timeout_s: float = _DEFAULT_SNAPSHOT_FILESYSTEM_TIMEOUT_S + + +class ModalSandboxSession(BaseSandboxSession): + """ + SandboxSession implementation backed by a Modal Sandbox. + """ + + state: ModalSandboxSessionState + + _sandbox: modal.Sandbox | None + _image: modal.Image | None + _running: bool + + def __init__( + self, + *, + state: ModalSandboxSessionState, + # Optional in-memory handles. These are not guaranteed to be resumable; state holds ids. + image: modal.Image | None = None, + sandbox: modal.Sandbox | None = None, + ) -> None: + self.state = state + self._image = image + self._sandbox = sandbox + if image is not None: + self.state.image_id = getattr(image, "object_id", self.state.image_id) + if sandbox is not None: + self.state.sandbox_id = getattr(sandbox, "object_id", self.state.sandbox_id) + self._running = False + + @classmethod + def from_state( + cls, + state: ModalSandboxSessionState, + *, + image: modal.Image | None = None, + sandbox: modal.Sandbox | None = None, + ) -> ModalSandboxSession: + return cls(state=state, image=image, sandbox=sandbox) + + async def _call_modal( + self, + fn: Callable[..., R], + *args: object, + call_timeout: float | None = None, + **kwargs: object, + ) -> R: + """ + Prefer Modal's async interface (`fn.aio(...)`) when available. + + Falls back to running the blocking call in a thread to preserve compatibility + with SDK surfaces that do not expose `.aio`. + """ + + aio_fn = getattr(fn, "aio", None) + if callable(aio_fn): + coro = cast(Awaitable[R], aio_fn(*args, **kwargs)) + else: + loop = asyncio.get_running_loop() + bound = functools.partial(fn, *args, **kwargs) + coro = loop.run_in_executor(None, bound) + if call_timeout is None: + return await coro + return await asyncio.wait_for(coro, timeout=call_timeout) + + async def start(self) -> None: + try: + # Ensure workspace root exists before SandboxSession.start() needs it. + await self.exec("mkdir", "-p", "--", str(Path(self.state.manifest.root)), shell=False) + except Exception as e: + raise WorkspaceStartError(path=Path(self.state.manifest.root), cause=e) from e + + self._running = True + await super().start() + + async def stop(self) -> None: + try: + await super().stop() + except Exception as e: + raise WorkspaceStopError(path=Path(self.state.manifest.root), cause=e) from e + + async def shutdown(self) -> None: + terminated = False + try: + sandbox = self._sandbox + if sandbox is not None: + await self._call_modal(sandbox.terminate, call_timeout=5.0) + terminated = True + elif self.state.sandbox_id: + sid = self.state.sandbox_id + assert sid is not None + sb = await self._call_modal(modal.Sandbox.from_id, sid, call_timeout=10.0) + await self._call_modal(sb.terminate, call_timeout=5.0) + terminated = True + except Exception: + pass + finally: + if terminated: + self.state.sandbox_id = None + self._sandbox = None + self._running = False + + async def _ensure_sandbox(self) -> None: + if self._sandbox is not None: + return + + # If resuming, try to rehydrate the sandbox handle from the persisted id. + sid = self.state.sandbox_id + if sid: + try: + sb = await self._call_modal(modal.Sandbox.from_id, sid, call_timeout=10.0) + + # `poll()` returns an exit code when the sandbox is terminated, else None. + poll_result = await self._call_modal(sb.poll, call_timeout=5.0) + is_running = poll_result is None + if is_running: + self._sandbox = sb + return + except Exception: + pass + + # Resumed sandbox handle is dead or invalid; clear and create a fresh one. + self._sandbox = None + self.state.sandbox_id = None + + app = await self._call_modal( + modal.App.lookup, + self.state.app_name, + create_if_missing=True, + call_timeout=10.0, + ) + if not self._image: + image_id = self.state.image_id + if image_id: + self._image = await self._call_modal( + modal.Image.from_id, image_id, call_timeout=30.0 + ) + else: + tag = self.state.image_tag + if not isinstance(tag, str) or not tag: + tag = _DEFAULT_IMAGE_TAG + # Record the default for better debuggability/resume. + self.state.image_tag = tag + self._image = await self._call_modal( + modal.Image.from_registry, tag, call_timeout=30.0 + ) + + manifest_envs = cast(dict[str, str | None], await self.state.manifest.environment.resolve()) + self._sandbox = await self._call_modal( + modal.Sandbox.create, + app=app, + image=self._image, + workdir=self.state.manifest.root, + env=manifest_envs, + call_timeout=self.state.sandbox_create_timeout_s, + ) + + # Persist sandbox id for future resume. + assert self._sandbox is not None + self.state.sandbox_id = self._sandbox.object_id + + assert self._image is not None + self.state.image_id = self._image.object_id + + async def _exec_internal( + self, *command: str | Path, timeout: float | None = None + ) -> ExecResult: + await self._ensure_sandbox() + assert self._sandbox is not None + + modal_timeout: int | None = None + if timeout is not None: + # Modal's Sandbox.exec timeout is integer seconds; use ceil so the command + # is guaranteed to be terminated server-side at or before our timeout window + # (modulo 1s granularity). + modal_timeout = int(max(_DEFAULT_TIMEOUT_S, math.ceil(timeout))) + + def _run() -> ExecResult: + assert self._sandbox is not None + try: + argv: tuple[str, ...] = tuple(str(part) for part in command) + proc: ContainerProcess[bytes] = self._sandbox.exec( + *argv, + text=False, + timeout=modal_timeout, + ) + # Drain full output; Modal buffers process output server-side. + stdout = proc.stdout.read() + stderr = proc.stderr.read() + exit_code = proc.wait() + return ExecResult( + stdout=stdout or b"", stderr=stderr or b"", exit_code=exit_code or 0 + ) + except Exception as e: + raise e + + try: + return cast(ExecResult, await self._call_modal(_run, call_timeout=timeout)) + except asyncio.TimeoutError as e: + # The worker thread continues running; prevent background mutations by terminating + # the sandbox and clearing our handle. + sandbox = self._sandbox + if sandbox is not None: + try: + await self._call_modal(sandbox.terminate, call_timeout=5.0) + except Exception: + pass + self._sandbox = None + self.state.sandbox_id = None + self._running = False + raise ExecTimeoutError(command=command, timeout_s=timeout, cause=e) from e + except ExecTimeoutError: + raise + except Exception as e: + raise ExecTransportError(command=command, cause=e) from e + + async def read(self, path: Path) -> io.IOBase: + # Read by `cat` so the payload is returned as bytes. + workspace_path = resolve_workspace_path( + Path(self.state.manifest.root), + path, + allow_absolute_within_root=True, + ) + cmd = ["sh", "-lc", f"cat -- {shlex.quote(str(workspace_path))}"] + try: + out = await self.exec(*cmd, shell=False) + except ExecTimeoutError as e: + raise WorkspaceArchiveReadError(path=workspace_path, cause=e) from e + except ExecTransportError as e: + raise WorkspaceArchiveReadError(path=workspace_path, cause=e) from e + + if not out.ok(): + raise WorkspaceReadNotFoundError( + path=path, context={"stderr": out.stderr.decode("utf-8", "replace")} + ) + + return io.BytesIO(out.stdout) + + async def write(self, path: Path, data: io.IOBase) -> None: + payload = data.read() + if isinstance(payload, str): + payload = payload.encode("utf-8") + if not isinstance(payload, (bytes, bytearray)): + raise WorkspaceWriteTypeError(path=path, actual_type=type(payload).__name__) + + await self._ensure_sandbox() + assert self._sandbox is not None + + workspace_path = resolve_workspace_path( + Path(self.state.manifest.root), + path, + allow_absolute_within_root=True, + ) + + def _run() -> None: + assert self._sandbox is not None + # Ensure parent directory exists. + parent = str(workspace_path.parent) + self._sandbox.exec("mkdir", "-p", "--", parent, text=False).wait() + + # Stream bytes into `cat > file` to avoid quoting/binary issues. + cmd = ["sh", "-lc", f"cat > {shlex.quote(str(workspace_path))}"] + proc = self._sandbox.exec(*cmd, text=False) + proc.stdin.write(bytes(payload)) + proc.stdin.write_eof() + proc.stdin.drain() + exit_code = proc.wait() + if exit_code != 0: + stderr: bytes = proc.stderr.read() + raise WorkspaceArchiveWriteError( + path=workspace_path, + context={ + "reason": "write_nonzero_exit", + "exit_code": exit_code, + "stderr": stderr.decode("utf-8", "replace"), + }, + ) + + try: + await self._call_modal(_run, call_timeout=30.0) + except WorkspaceArchiveWriteError: + raise + except Exception as e: + raise WorkspaceArchiveWriteError(path=workspace_path, cause=e) from e + + async def running(self) -> bool: + if not self._running or self._sandbox is None: + return False + + try: + assert self._sandbox is not None + poll_result = await self._call_modal(self._sandbox.poll, call_timeout=5.0) + return poll_result is None + except Exception: + return False + + async def persist_workspace(self) -> io.IOBase: + if self.state.workspace_persistence == _WORKSPACE_PERSISTENCE_SNAPSHOT_FILESYSTEM: + return await self._persist_workspace_via_snapshot_filesystem() + return await self._persist_workspace_via_tar() + + async def hydrate_workspace(self, data: io.IOBase) -> None: + if self.state.workspace_persistence == _WORKSPACE_PERSISTENCE_SNAPSHOT_FILESYSTEM: + return await self._hydrate_workspace_via_snapshot_filesystem(data) + return await self._hydrate_workspace_via_tar(data) + + async def _persist_workspace_via_snapshot_filesystem(self) -> io.IOBase: + """ + Persist the workspace using Modal's snapshot_filesystem API when available. + + Modal's snapshot_filesystem is expected to return a snapshot reference + (typically a Modal object such as an Image/Snapshot handle, or an id + string). We serialize a small reference envelope that + `_hydrate_workspace_via_snapshot_filesystem` can interpret. + """ + + root = Path(self.state.manifest.root) + await self._ensure_sandbox() + assert self._sandbox is not None + + sandbox = self._sandbox + if not hasattr(sandbox, "snapshot_filesystem"): + # Feature not present in this Modal SDK version; fall back to tar implementation. + return await self._persist_workspace_via_tar() + + skip = self.state.manifest.ephemeral_entry_paths() + + # Modal's snapshot_filesystem does not support excluding paths. To + # preserve the semantics of "ephemeral manifest entries are not + # persisted", we temporarily remove those paths, snapshot, then + # restore them back into the running session. + skip_abs = [root / rel for rel in sorted(skip, key=lambda p: p.as_posix())] + ephemeral_backup: bytes | None = None + if skip_abs: + # Best-effort: tar up the ephemeral paths (if they exist). We run + # via shell so missing paths do not cause a hard failure + # (`|| true`). + rel_args = " ".join(shlex.quote(p.relative_to(root).as_posix()) for p in skip_abs) + cmd = f"cd -- {shlex.quote(str(root))} && (tar cf - -- {rel_args} 2>/dev/null || true)" + out = await self.exec("sh", "-lc", cmd, shell=False) + ephemeral_backup = out.stdout or b"" + + # Remove ephemeral paths before snapshot so they are not captured. + rm_cmd = ["rm", "-rf", "--", *[str(p) for p in skip_abs]] + _ = await self.exec(*rm_cmd, shell=False) + + restore_error: WorkspaceArchiveReadError | None = None + + async def _restore_ephemeral_paths() -> WorkspaceArchiveReadError | None: + if not ephemeral_backup: + return None + + backup = bytes(ephemeral_backup) + + def _restore_ephemeral() -> None: + assert self._sandbox is not None + proc = self._sandbox.exec("tar", "xf", "-", "-C", str(root), text=False) + proc.stdin.write(backup) + proc.stdin.write_eof() + proc.stdin.drain() + exit_code = proc.wait() + if exit_code != 0: + stderr: bytes = proc.stderr.read() + raise WorkspaceArchiveReadError( + path=root, + context={ + "reason": "snapshot_filesystem_ephemeral_restore_failed", + "exit_code": exit_code, + "stderr": stderr.decode("utf-8", "replace"), + }, + ) + + try: + await self._call_modal( + _restore_ephemeral, + call_timeout=self.state.snapshot_filesystem_restore_timeout_s, + ) + except WorkspaceArchiveReadError as exc: + return exc + except Exception as exc: + return WorkspaceArchiveReadError( + path=root, + context={"reason": "snapshot_filesystem_ephemeral_restore_failed"}, + cause=exc, + ) + return None + + try: + snap = await self._call_modal( + sandbox.snapshot_filesystem, + call_timeout=self.state.snapshot_filesystem_timeout_s, + ) + except Exception as e: + restore_error = await _restore_ephemeral_paths() + if restore_error is not None: + logger.warning( + "Failed to restore Modal ephemeral paths after snapshot failure: %s", + restore_error, + ) + raise WorkspaceArchiveReadError( + path=root, context={"reason": "snapshot_filesystem_failed"}, cause=e + ) from e + + if isinstance(snap, (bytes, bytearray)): + # should never happen, just a safe guardrail + raise WorkspaceArchiveReadError( + path=root, + context={ + "reason": "snapshot_filesystem_unexpected_bytes", + "type": type(snap).__name__, + }, + ) + + # Snapshot is expected to be a Modal Image (or a compatible handle with an object_id). + if not hasattr(snap, "object_id") and not isinstance(snap, str): + raise WorkspaceArchiveReadError( + path=root, + context={ + "reason": "snapshot_filesystem_unexpected_return", + "type": type(snap).__name__, + }, + ) + + restore_error = await _restore_ephemeral_paths() + if restore_error is not None: + raise restore_error + + snapshot_id: str | None = None + if isinstance(snap, str): + snapshot_id = snap + else: + snapshot_id = getattr(snap, "object_id", None) or getattr(snap, "id", None) + if snapshot_id is not None and not isinstance(snapshot_id, str): + snapshot_id = None + + if not snapshot_id: + raise WorkspaceArchiveReadError( + path=root, + context={ + "reason": "snapshot_filesystem_unexpected_return", + "type": type(snap).__name__, + }, + ) + + return io.BytesIO(_encode_snapshot_filesystem_ref(snapshot_id=snapshot_id)) + + @retry_async( + retry_if=lambda exc, self: exception_chain_contains_type(exc, (ExecTransportError,)) + or exception_chain_has_status_code(exc, TRANSIENT_HTTP_STATUS_CODES) + ) + async def _persist_workspace_via_tar(self) -> io.IOBase: + # Existing tar implementation extracted so snapshot_filesystem mode can fall back cleanly. + root = Path(self.state.manifest.root) + skip = self.state.manifest.ephemeral_entry_paths() + + excludes: list[str] = [] + for rel in sorted(skip, key=lambda p: p.as_posix()): + excludes.extend(["--exclude", f"./{rel.as_posix().lstrip('./')}"]) + + cmd: list[str] = [ + "tar", + "cf", + "-", + *excludes, + "-C", + str(root), + ".", + ] + + try: + out = await self.exec(*cmd, shell=False) + if not out.ok(): + raise WorkspaceArchiveReadError( + path=root, + context={ + "reason": "tar_nonzero_exit", + "exit_code": out.exit_code, + "stderr": out.stderr.decode("utf-8", "replace"), + }, + ) + return io.BytesIO(out.stdout) + except WorkspaceArchiveReadError: + raise + except Exception as e: + raise WorkspaceArchiveReadError(path=root, cause=e) from e + + async def _hydrate_workspace_via_snapshot_filesystem(self, data: io.IOBase) -> None: + """ + Hydrate using Modal's snapshot_filesystem restore API when the + persisted payload is a snapshot ref. Otherwise, fall back to tar + extraction (to support SDKs that return tar bytes). + """ + + root = Path(self.state.manifest.root) + raw = data.read() + if isinstance(raw, str): + raw = raw.encode("utf-8") + if not isinstance(raw, (bytes, bytearray)): + raise WorkspaceArchiveWriteError(path=root, context={"reason": "non_bytes_payload"}) + + snapshot_id = _decode_snapshot_filesystem_ref(bytes(raw)) + if snapshot_id is None: + # Not an envelope; treat as tar payload. + return await self._hydrate_workspace_via_tar(io.BytesIO(bytes(raw))) + if not snapshot_id: + raise WorkspaceArchiveWriteError( + path=root, context={"reason": "snapshot_filesystem_invalid_snapshot_id"} + ) + + # Best-effort: if a sandbox already exists, terminate it to avoid leaking resources. + # We want the restored snapshot image to define the new sandbox filesystem. + prior = self._sandbox + if prior is not None: + try: + await self._call_modal(prior.terminate, call_timeout=5.0) + except Exception: + pass + finally: + self._sandbox = None + self.state.sandbox_id = None + + manifest_envs = cast(dict[str, str | None], await self.state.manifest.environment.resolve()) + + def _run_restore() -> None: + # Rehydrate an image from the snapshot id. + image = modal.Image.from_id(snapshot_id) + + # Prefer the existing app-based sandbox creation signature to match `_ensure_sandbox`. + app = modal.App.lookup(self.state.app_name, create_if_missing=True) + + try: + sb = modal.Sandbox.create( + app=app, + image=image, + workdir=self.state.manifest.root, + env=manifest_envs, + ) + except TypeError: + # Older/newer SDKs may not accept app/workdir; fall back to simpler signatures. + try: + sb = modal.Sandbox.create( + name=self.state.app_name, + image=image, + env=manifest_envs, + ) + except TypeError: + sb = modal.Sandbox.create( + image=image, + env=manifest_envs, + ) + + # Ensure workspace root exists even if the image does not contain it. + try: + sb.exec("mkdir", "-p", "--", str(root), text=False).wait() + except Exception: + pass + + # Update in-memory handles and persisted ids. + self._image = image + self.state.image_id = getattr(image, "object_id", None) or snapshot_id + self._sandbox = sb + self.state.sandbox_id = getattr(sb, "object_id", None) + + try: + await self._call_modal( + _run_restore, call_timeout=self.state.snapshot_filesystem_restore_timeout_s + ) + except Exception as e: + raise WorkspaceArchiveWriteError( + path=root, + context={ + "reason": "snapshot_filesystem_restore_failed", + "snapshot_id": snapshot_id, + }, + cause=e, + ) from e + + async def _hydrate_workspace_via_tar(self, data: io.IOBase) -> None: + root = Path(self.state.manifest.root) + + raw = data.read() + if isinstance(raw, str): + raw = raw.encode("utf-8") + if not isinstance(raw, (bytes, bytearray)): + raise WorkspaceArchiveWriteError(path=root, context={"reason": "non_bytes_tar_payload"}) + + try: + with tarfile.open(fileobj=io.BytesIO(bytes(raw)), mode="r:*") as tar: + for member in tar.getmembers(): + name = member.name + if name in ("", ".", "./"): + continue + if should_skip_tar_member( + name, + skip_rel_paths=self.state.manifest.ephemeral_entry_paths(), + root_name=None, + ): + continue + # Mirror tar_utils safety checks (no extraction here). + if Path(name).is_absolute(): + raise UnsafeTarMemberError(member=name, reason="absolute path") + if ".." in Path(name).parts: + raise UnsafeTarMemberError(member=name, reason="parent traversal") + if member.issym() or member.islnk(): + raise UnsafeTarMemberError(member=name, reason="link member not allowed") + if not (member.isdir() or member.isreg()): + raise UnsafeTarMemberError(member=name, reason="unsupported member type") + except UnsafeTarMemberError as e: + raise WorkspaceArchiveWriteError( + path=root, context={"reason": e.reason, "member": e.member}, cause=e + ) from e + except (tarfile.TarError, OSError) as e: + raise WorkspaceArchiveWriteError(path=root, cause=e) from e + + await self._ensure_sandbox() + assert self._sandbox is not None + + def _run() -> None: + assert self._sandbox is not None + self._sandbox.exec("mkdir", "-p", "--", str(root), text=False).wait() + proc = self._sandbox.exec("tar", "xf", "-", "-C", str(root), text=False) + proc.stdin.write(bytes(raw)) + proc.stdin.write_eof() + proc.stdin.drain() + exit_code = proc.wait() + if exit_code != 0: + stderr: bytes = proc.stderr.read() + raise WorkspaceArchiveWriteError( + path=root, + context={ + "reason": "tar_extract_nonzero_exit", + "exit_code": exit_code, + "stderr": stderr.decode("utf-8", "replace"), + }, + ) + + try: + await self._call_modal(_run, call_timeout=60.0) + except WorkspaceArchiveWriteError: + raise + except Exception as e: + raise WorkspaceArchiveWriteError(path=root, cause=e) from e + + +class ModalSandboxClient(BaseSandboxClient[ModalSandboxClientOptions]): + backend_id = "modal" + _default_image: ModalImageSelector | None + _default_sandbox: ModalSandboxSelector | None + _instrumentation: Instrumentation + + def __init__( + self, + *, + image: ModalImageSelector | None = None, + sandbox: ModalSandboxSelector | None = None, + instrumentation: Instrumentation | None = None, + dependencies: Dependencies | None = None, + ) -> None: + self._default_image = image + self._default_sandbox = sandbox + self._instrumentation = instrumentation or Instrumentation() + self._dependencies = dependencies + + async def create( + self, + *, + snapshot: SnapshotSpec | None = None, + manifest: Manifest | None = None, + options: ModalSandboxClientOptions, + ) -> SandboxSession: + """ + Create a new Modal-backed session. + + Expected options: + - app_name: str (required) + - sandbox_create_timeout_s: float | None (async timeout for sandbox creation call) + - workspace_persistence: Literal["tar", "snapshot_filesystem"] (optional) + - snapshot_filesystem_timeout_s: float | None + (async timeout for snapshot_filesystem call) + - snapshot_filesystem_restore_timeout_s: float | None + (async timeout for snapshot restore call) + """ + + if options is None: + raise ValueError("ModalSandboxClient.create requires options with app_name") + app_name = options.app_name + if not app_name: + raise ValueError("ModalSandboxClient.create requires a valid app_name") + + image_sel = self._default_image + + sandbox_sel = self._default_sandbox + + sandbox_create_timeout_s = options.sandbox_create_timeout_s + if sandbox_create_timeout_s is not None and not isinstance( + sandbox_create_timeout_s, (int, float) + ): + raise ValueError( + "ModalSandboxClient.create requires sandbox_create_timeout_s to be a number" + ) + + workspace_persistence = options.workspace_persistence + if workspace_persistence not in ( + _WORKSPACE_PERSISTENCE_TAR, + _WORKSPACE_PERSISTENCE_SNAPSHOT_FILESYSTEM, + ): + raise ValueError( + "ModalSandboxClient.create requires workspace_persistence to be one of " + f"{_WORKSPACE_PERSISTENCE_TAR!r} or {_WORKSPACE_PERSISTENCE_SNAPSHOT_FILESYSTEM!r}" + ) + + snapshot_filesystem_timeout_s = options.snapshot_filesystem_timeout_s + if snapshot_filesystem_timeout_s is not None and not isinstance( + snapshot_filesystem_timeout_s, (int, float) + ): + raise ValueError( + "ModalSandboxClient.create requires snapshot_filesystem_timeout_s to be a number" + ) + + snapshot_filesystem_restore_timeout_s = options.snapshot_filesystem_restore_timeout_s + if snapshot_filesystem_restore_timeout_s is not None and not isinstance( + snapshot_filesystem_restore_timeout_s, (int, float) + ): + raise ValueError( + "ModalSandboxClient.create requires " + "snapshot_filesystem_restore_timeout_s to be a number" + ) + + if manifest is None: + manifest = Manifest() + + session_id = uuid.uuid4() + state_image_id: str | None = None + state_image_tag: str | None = None + session_image: modal.Image | None = None + if image_sel is not None: + if image_sel.kind == "image": + if not isinstance(image_sel.value, modal.Image): + raise ValueError( + "ModalSandboxClient.__init__ requires image to be a modal.Image" + ) + session_image = image_sel.value + state_image_id = getattr(session_image, "object_id", None) + elif image_sel.kind == "id": + if not isinstance(image_sel.value, str) or not image_sel.value: + raise ValueError( + "ModalSandboxClient.__init__ requires image_id to be a non-empty string" + ) + state_image_id = image_sel.value + else: + if not isinstance(image_sel.value, str) or not image_sel.value: + raise ValueError( + "ModalSandboxClient.__init__ requires image_tag to be a non-empty string" + ) + state_image_tag = image_sel.value + + state_sandbox_id: str | None = None + session_sandbox: modal.Sandbox | None = None + if sandbox_sel is not None: + if sandbox_sel.kind == "sandbox": + if not isinstance(sandbox_sel.value, modal.Sandbox): + raise ValueError( + "ModalSandboxClient.__init__ requires sandbox to be a modal.Sandbox" + ) + session_sandbox = sandbox_sel.value + state_sandbox_id = getattr(session_sandbox, "object_id", None) + else: + if not isinstance(sandbox_sel.value, str) or not sandbox_sel.value: + raise ValueError( + "ModalSandboxClient.__init__ requires sandbox_id to be a non-empty string" + ) + state_sandbox_id = sandbox_sel.value + + snapshot_id = str(session_id) + snapshot_instance = resolve_snapshot(snapshot, snapshot_id) + state = ModalSandboxSessionState( + session_id=session_id, + manifest=manifest, + snapshot=snapshot_instance, + app_name=app_name, + image_tag=state_image_tag, + image_id=state_image_id, + sandbox_id=state_sandbox_id, + workspace_persistence=workspace_persistence, + ) + if sandbox_create_timeout_s is not None: + state.sandbox_create_timeout_s = float(sandbox_create_timeout_s) + if snapshot_filesystem_timeout_s is not None: + state.snapshot_filesystem_timeout_s = float(snapshot_filesystem_timeout_s) + if snapshot_filesystem_restore_timeout_s is not None: + state.snapshot_filesystem_restore_timeout_s = float( + snapshot_filesystem_restore_timeout_s + ) + + # Pass the in-memory handles through to the session (they may not be resumable). + inner = ModalSandboxSession.from_state( + state, + image=session_image, + sandbox=session_sandbox, + ) + return self._wrap_session(inner, instrumentation=self._instrumentation) + + async def delete(self, session: SandboxSession) -> SandboxSession: + """ + Best-effort cleanup of Modal sandbox resources. + """ + + inner = session._inner + if not isinstance(inner, ModalSandboxSession): + raise TypeError("ModalSandboxClient.delete expects a ModalSandboxSession") + + # Prefer the live handle if present. + sandbox = getattr(inner, "_sandbox", None) + try: + if sandbox is not None: + await asyncio.get_running_loop().run_in_executor(None, sandbox.terminate) + return session + except Exception: + return session + + # Otherwise, best-effort terminate via sandbox_id. + sid = inner.state.sandbox_id + if sid: + try: + sb = await asyncio.get_running_loop().run_in_executor( + None, lambda: modal.Sandbox.from_id(sid) + ) + await asyncio.get_running_loop().run_in_executor(None, sb.terminate) + except Exception: + pass + + return session + + async def resume(self, state: SandboxSessionState) -> SandboxSession: + if not isinstance(state, ModalSandboxSessionState): + raise TypeError("ModalSandboxClient.resume expects a ModalSandboxSessionState") + inner = ModalSandboxSession.from_state(state) + return self._wrap_session(inner, instrumentation=self._instrumentation) + + def deserialize_session_state(self, payload: dict[str, object]) -> SandboxSessionState: + return ModalSandboxSessionState.model_validate(payload) diff --git a/src/agents/result.py b/src/agents/result.py index 774c90dc4e..b240f9c95c 100644 --- a/src/agents/result.py +++ b/src/agents/result.py @@ -46,7 +46,9 @@ ) if TYPE_CHECKING: - pass + from collections.abc import Awaitable, Callable + + from .sandbox.session.base_sandbox_session import BaseSandboxSession T = TypeVar("T") @@ -78,6 +80,7 @@ def _populate_state_from_result( auto_previous_response_id: bool = False, ) -> RunState[Any]: """Populate a RunState with common fields from a RunResult.""" + state._current_agent = result.last_agent model_input_items = getattr(result, "_model_input_items", None) if isinstance(model_input_items, list): state._generated_items = list(model_input_items) @@ -106,6 +109,11 @@ def _populate_state_from_result( if trace_state is None: trace_state = TraceState.from_trace(getattr(result, "trace", None)) state._trace_state = copy.deepcopy(trace_state) if trace_state else None + sandbox_resume_state = getattr(result, "_sandbox_resume_state", None) + if isinstance(sandbox_resume_state, dict): + state._sandbox = copy.deepcopy(sandbox_resume_state) + else: + state._sandbox = None return state @@ -144,6 +152,20 @@ def _input_items_for_result( return run_items_to_input_items(model_input_items, reasoning_item_id_policy) +def _starting_agent_for_state(result: RunResultBase) -> Agent[Any]: + """Return the root agent graph that should seed RunState identity resolution.""" + state = getattr(result, "_state", None) + starting_agent = getattr(state, "_starting_agent", None) + if isinstance(starting_agent, Agent): + return starting_agent + + stored_starting_agent = getattr(result, "_starting_agent_for_state", None) + if isinstance(stored_starting_agent, Agent): + return stored_starting_agent + + return result.last_agent + + @dataclass class RunResultBase(abc.ABC): input: str | list[TResponseInputItem] @@ -185,6 +207,12 @@ class RunResultBase(abc.ABC): This is only set when the runner preserved extra session history items that should not be replayed into the next local run, such as nested handoff history or filtered handoff input. """ + _sandbox_resume_state: dict[str, object] | None = field(default=None, init=False, repr=False) + """Serialized sandbox session state captured during the run.""" + _sandbox_session: BaseSandboxSession | None = field(default=None, init=False, repr=False) + """Live sandbox session attached to this run result when sandbox execution is enabled.""" + _starting_agent_for_state: Agent[Any] | None = field(default=None, init=False, repr=False) + """Root agent graph used when converting the result back into RunState.""" @classmethod def __get_pydantic_core_schema__( @@ -385,7 +413,7 @@ def to_state(self) -> RunState[Any]: original_input=original_input_for_state if original_input_for_state is not None else self.input, - starting_agent=self.last_agent, + starting_agent=_starting_agent_for_state(self), max_turns=self.max_turns, ) @@ -493,6 +521,13 @@ class RunResultStreaming(RunResultBase): ) """How reasoning IDs should be represented when converting to input history.""" _run_impl_task: InitVar[asyncio.Task[Any] | None] = None + _sandbox_cleanup: Callable[[], Awaitable[None]] | None = field( + default=None, + init=False, + repr=False, + ) + _sandbox_cleanup_task: asyncio.Task[None] | None = field(default=None, init=False, repr=False) + _sandbox_cleanup_callback_registered: bool = field(default=False, init=False, repr=False) def __post_init__(self, _run_impl_task: asyncio.Task[Any] | None) -> None: self._current_agent_ref = weakref.ref(self.current_agent) @@ -525,6 +560,57 @@ def _release_last_agent_reference(self) -> None: # Preserve dataclass field so repr/asdict continue to succeed. self.__dict__["current_agent"] = None + async def _run_sandbox_cleanup(self) -> None: + sandbox_cleanup = self._sandbox_cleanup + if sandbox_cleanup is None: + return + + task = self._sandbox_cleanup_task + if task is None: + + async def _cleanup_once() -> None: + try: + await sandbox_cleanup() + except Exception as error: + logger.warning( + "Failed to clean up sandbox resources after streamed run: %s", error + ) + + task = asyncio.create_task(_cleanup_once()) + self._sandbox_cleanup_task = task + + await task + + def ensure_sandbox_cleanup_on_completion(self) -> None: + if ( + self._sandbox_cleanup is None + or self.run_loop_task is None + or self._sandbox_cleanup_callback_registered + ): + return + + original_task = self.run_loop_task + self._sandbox_cleanup_callback_registered = True + original_task.add_done_callback( + lambda _task: asyncio.create_task(self._run_sandbox_cleanup()) + ) + + async def _await_run_and_cleanup() -> Any: + try: + result = await original_task + except asyncio.CancelledError: + if not original_task.done(): + original_task.cancel() + raise + except Exception: + await self._run_sandbox_cleanup() + raise + + await self._run_sandbox_cleanup() + return result + + self.run_loop_task = asyncio.create_task(_await_run_and_cleanup()) + def cancel(self, mode: Literal["immediate", "after_turn"] = "immediate") -> None: """Cancel the streaming run. @@ -622,24 +708,28 @@ async def stream_events(self) -> AsyncIterator[StreamEvent]: yield item self._event_queue.task_done() finally: - if cancelled: - # Cancellation should return promptly, so avoid waiting on long-running tasks. - # Tasks have already been cancelled above. - self._cleanup_tasks() - else: - # Ensure main execution completes before cleanup to avoid race conditions - # with session operations - await self._await_task_safely(self.run_loop_task) - # Safely terminate all background tasks after main execution has finished - self._cleanup_tasks() - - # Allow any pending callbacks (e.g., cancellation handlers) to enqueue their - # completion sentinels before we clear the queues for observability. - await asyncio.sleep(0) - - # Drain queues so callers observing internal state see them empty after completion. - self._drain_event_queue() - self._drain_input_guardrail_queue() + try: + if cancelled: + # Cancellation should return promptly, so avoid waiting on long-running tasks. + # Tasks have already been cancelled above. + self._cleanup_tasks() + else: + # Ensure main execution completes before cleanup to avoid race conditions + # with session operations. + await self._await_task_safely(self.run_loop_task) + # Safely terminate all background tasks after main execution has finished. + self._cleanup_tasks() + + if not cancelled: + await self._run_sandbox_cleanup() + finally: + # Allow any pending callbacks (e.g., cancellation handlers) to enqueue their + # completion sentinels before we clear the queues for observability. + await asyncio.sleep(0) + + # Drain queues so callers observing internal state see them empty after completion. + self._drain_event_queue() + self._drain_input_guardrail_queue() if self._stored_exception: raise self._stored_exception @@ -781,7 +871,7 @@ def to_state(self) -> RunState[Any]: state = RunState( context=self.context_wrapper, original_input=self._original_input if self._original_input is not None else self.input, - starting_agent=self.last_agent, + starting_agent=_starting_agent_for_state(self), max_turns=self.max_turns, ) diff --git a/src/agents/run.py b/src/agents/run.py index 047d454d35..de808cb5dd 100644 --- a/src/agents/run.py +++ b/src/agents/run.py @@ -43,6 +43,7 @@ ) from .run_context import RunContextWrapper, TContext from .run_error_handlers import RunErrorHandlers +from .run_internal.agent_bindings import bind_public_agent from .run_internal.agent_runner_helpers import ( append_model_response_if_new, apply_resumed_conversation_settings, @@ -106,6 +107,7 @@ serialize_tool_use_tracker, ) from .run_state import RunState +from .sandbox.runtime import SandboxRuntime from .tool import dispose_resolved_computers from .tool_guardrails import ToolInputGuardrailResult, ToolOutputGuardrailResult from .tracing import Span, SpanError, agent_span, get_current_trace @@ -583,12 +585,45 @@ async def run( run_state._reasoning_item_id_policy = resolved_reasoning_item_id_policy run_state.set_trace(get_current_trace()) + sandbox_runtime = SandboxRuntime( + starting_agent=starting_agent, + run_config=run_config, + run_state=run_state, + ) + + completed_result: RunResult | None = None + def _with_reasoning_item_id_policy(result: RunResult) -> RunResult: result._reasoning_item_id_policy = resolved_reasoning_item_id_policy if run_state is not None: run_state._reasoning_item_id_policy = resolved_reasoning_item_id_policy return result + def _tool_use_tracker_snapshot() -> dict[str, list[str]]: + identity_root_agent = starting_agent + if run_state is not None and run_state._starting_agent is not None: + identity_root_agent = run_state._starting_agent + return serialize_tool_use_tracker( + tool_use_tracker, + starting_agent=identity_root_agent, + ) + + def _finalize_result(result: RunResult) -> RunResult: + nonlocal completed_result + result._starting_agent_for_state = ( + run_state._starting_agent + if run_state is not None and run_state._starting_agent is not None + else starting_agent + ) + finalized_result = finalize_conversation_tracking( + _with_reasoning_item_id_policy(result), + server_conversation_tracker=server_conversation_tracker, + run_state=run_state, + ) + sandbox_runtime.apply_result_metadata(finalized_result) + completed_result = finalized_result + return finalized_result + pending_server_items: list[RunItem] | None = None input_guardrail_results: list[InputGuardrailResult] = ( list(run_state._input_guardrail_results) if run_state is not None else [] @@ -609,6 +644,7 @@ def _with_reasoning_item_id_policy(result: RunResult) -> RunResult: current_agent = run_state._current_agent else: current_agent = starting_agent + sandbox_runtime.assert_agent_supported(current_agent) should_run_agent_start_hooks = True store_setting = current_agent.model_settings.resolve(run_config.model_settings).store @@ -618,11 +654,16 @@ def _with_reasoning_item_id_policy(result: RunResult) -> RunResult: and original_user_input is not None and session_input_items_for_persistence is None ): + sandbox_runtime.assert_agent_supported(current_agent) session_input_items_for_persistence = ItemHelpers.input_to_new_input_list( original_user_input ) - if session_persistence_enabled and session_input_items_for_persistence: + if ( + session_persistence_enabled + and session_input_items_for_persistence + and not sandbox_runtime.enabled + ): # Capture the exact input saved so it can be rewound on conversation lock retries. last_saved_input_snapshot_for_rewind = list(session_input_items_for_persistence) await save_result_to_session( @@ -637,6 +678,22 @@ def _with_reasoning_item_id_policy(result: RunResult) -> RunResult: try: while True: resuming_turn = is_resumed_state + current_bindings = bind_public_agent(current_agent) + execution_agent = current_bindings.execution_agent + prepared_sandbox = await sandbox_runtime.prepare_agent( + current_agent=current_agent, + current_input=original_input, + context_wrapper=context_wrapper, + is_resumed_state=resuming_turn, + ) + current_bindings = prepared_sandbox.bindings + execution_agent = current_bindings.execution_agent + original_input = copy_input_items(prepared_sandbox.input) + if starting_input is not None and not isinstance(starting_input, RunState): + starting_input = copy_input_items(prepared_sandbox.input) + if run_state is not None: + run_state._original_input = copy_input_items(original_input) + normalized_starting_input: str | list[TResponseInputItem] = ( starting_input if starting_input is not None and not isinstance(starting_input, RunState) @@ -645,6 +702,18 @@ def _with_reasoning_item_id_policy(result: RunResult) -> RunResult: store_setting = current_agent.model_settings.resolve( run_config.model_settings ).store + if session_persistence_enabled and session_input_items_for_persistence: + last_saved_input_snapshot_for_rewind = list( + session_input_items_for_persistence + ) + await save_result_to_session( + session, + list(last_saved_input_snapshot_for_rewind), + [], + run_state, + store=store_setting, + ) + session_input_items_for_persistence = [] if run_state is not None and run_state._current_step is not None: if isinstance(run_state._current_step, NextStepInterruption): logger.debug("Continuing from interruption") @@ -655,7 +724,7 @@ def _with_reasoning_item_id_policy(result: RunResult) -> RunResult: raise UserError("No model response found in previous state") turn_result = await resolve_interrupted_turn( - agent=current_agent, + bindings=current_bindings, original_input=original_input, original_pre_step_items=generated_items, new_response=run_state._model_responses[-1], @@ -750,11 +819,7 @@ def _with_reasoning_item_id_policy(result: RunResult) -> RunResult: run_state=run_state, original_input=original_input, ) - return finalize_conversation_tracking( - _with_reasoning_item_id_policy(result), - server_conversation_tracker=server_conversation_tracker, - run_state=run_state, - ) + return _finalize_result(result) if isinstance(turn_result.next_step, NextStepRunAgain): continue @@ -791,9 +856,7 @@ def _with_reasoning_item_id_policy(result: RunResult) -> RunResult: tool_output_guardrail_results=tool_output_guardrail_results, context_wrapper=context_wrapper, interruptions=approvals_from_state, - _tool_use_tracker_snapshot=serialize_tool_use_tracker( - tool_use_tracker - ), + _tool_use_tracker_snapshot=_tool_use_tracker_snapshot(), max_turns=max_turns, ) result._current_turn = current_turn @@ -820,11 +883,7 @@ def _with_reasoning_item_id_policy(result: RunResult) -> RunResult: store=store_setting, ) result._original_input = copy_input_items(original_input) - return finalize_conversation_tracking( - _with_reasoning_item_id_policy(result), - server_conversation_tracker=server_conversation_tracker, - run_state=run_state, - ) + return _finalize_result(result) elif isinstance(turn_result.next_step, NextStepHandoff): current_agent = cast( Agent[TContext], turn_result.next_step.new_agent @@ -844,16 +903,17 @@ def _with_reasoning_item_id_policy(result: RunResult) -> RunResult: if run_state is not None: if run_state._current_step is None: run_state._current_step = NextStepRunAgain() # type: ignore[assignment] - all_tools = await get_all_tools(current_agent, context_wrapper) + all_tools = await get_all_tools(execution_agent, context_wrapper) await initialize_computer_tools( tools=all_tools, context_wrapper=context_wrapper ) if current_span is None: handoff_names = [ - h.agent_name for h in await get_handoffs(current_agent, context_wrapper) + h.agent_name + for h in await get_handoffs(execution_agent, context_wrapper) ] - if output_schema := get_output_schema(current_agent): + if output_schema := get_output_schema(execution_agent): output_type_name = output_schema.name() else: output_type_name = "str" @@ -932,7 +992,7 @@ def _with_reasoning_item_id_policy(result: RunResult) -> RunResult: tool_output_guardrail_results=tool_output_guardrail_results, context_wrapper=context_wrapper, interruptions=approvals_from_state, - _tool_use_tracker_snapshot=serialize_tool_use_tracker(tool_use_tracker), + _tool_use_tracker_snapshot=_tool_use_tracker_snapshot(), max_turns=max_turns, ) result._current_turn = max_turns @@ -957,11 +1017,7 @@ def _with_reasoning_item_id_policy(result: RunResult) -> RunResult: store=store_setting, ) result._original_input = copy_input_items(original_input) - return finalize_conversation_tracking( - _with_reasoning_item_id_policy(result), - server_conversation_tracker=server_conversation_tracker, - run_state=run_state, - ) + return _finalize_result(result) if run_state is not None and not resuming_turn: run_state._current_turn_persisted_item_count = 0 @@ -997,7 +1053,7 @@ def _with_reasoning_item_id_policy(result: RunResult) -> RunResult: sequential_results = await run_input_guardrails( starting_agent, sequential_guardrails, - copy_input_items(prepared_input), + copy_input_items(original_input), context_wrapper, ) except InputGuardrailTripwireTriggered: @@ -1016,7 +1072,7 @@ def _with_reasoning_item_id_policy(result: RunResult) -> RunResult: parallel_results: list[InputGuardrailResult] = [] model_task = asyncio.create_task( run_single_turn( - agent=current_agent, + bindings=current_bindings, all_tools=all_tools, original_input=original_input, generated_items=items_for_model, @@ -1042,7 +1098,7 @@ def _with_reasoning_item_id_policy(result: RunResult) -> RunResult: run_input_guardrails( starting_agent, parallel_guardrails, - copy_input_items(prepared_input), + copy_input_items(original_input), context_wrapper, ), model_task, @@ -1070,7 +1126,7 @@ def _with_reasoning_item_id_policy(result: RunResult) -> RunResult: input_guardrail_results.extend(parallel_results) else: turn_result = await run_single_turn( - agent=current_agent, + bindings=current_bindings, all_tools=all_tools, original_input=original_input, generated_items=items_for_model, @@ -1201,9 +1257,7 @@ def _with_reasoning_item_id_policy(result: RunResult) -> RunResult: tool_output_guardrail_results=tool_output_guardrail_results, context_wrapper=context_wrapper, interruptions=[], - _tool_use_tracker_snapshot=serialize_tool_use_tracker( - tool_use_tracker - ), + _tool_use_tracker_snapshot=_tool_use_tracker_snapshot(), max_turns=max_turns, ) result._current_turn = current_turn @@ -1225,11 +1279,7 @@ def _with_reasoning_item_id_policy(result: RunResult) -> RunResult: store=store_setting, ) result._original_input = copy_input_items(original_input) - return finalize_conversation_tracking( - _with_reasoning_item_id_policy(result), - server_conversation_tracker=server_conversation_tracker, - run_state=run_state, - ) + return _finalize_result(result) elif isinstance(turn_result.next_step, NextStepInterruption): if session_persistence_enabled: if not input_guardrails_triggered(input_guardrail_results): @@ -1286,11 +1336,7 @@ def _with_reasoning_item_id_policy(result: RunResult) -> RunResult: run_state=run_state, original_input=original_input, ) - return finalize_conversation_tracking( - _with_reasoning_item_id_policy(result), - server_conversation_tracker=server_conversation_tracker, - run_state=run_state, - ) + return _finalize_result(result) elif isinstance(turn_result.next_step, NextStepHandoff): current_agent = cast(Agent[TContext], turn_result.next_step.new_agent) if run_state is not None: @@ -1336,6 +1382,16 @@ def _with_reasoning_item_id_policy(result: RunResult) -> RunResult: ) raise finally: + try: + sandbox_resume_state = await sandbox_runtime.cleanup() + except Exception as error: + logger.warning("Failed to clean up sandbox resources after run: %s", error) + else: + if completed_result is not None: + completed_result._sandbox_resume_state = sandbox_resume_state + finally: + if completed_result is not None: + completed_result._sandbox_session = None try: await dispose_resolved_computers(run_context=context_wrapper) except Exception as error: @@ -1550,9 +1606,16 @@ def run_streamed( if run_state is not None: run_state.set_trace(new_trace or get_current_trace()) + sandbox_runtime = SandboxRuntime( + starting_agent=starting_agent, + run_config=run_config, + run_state=run_state, + ) + schema_agent = ( run_state._current_agent if run_state and run_state._current_agent else starting_agent ) + sandbox_runtime.assert_agent_supported(schema_agent) output_schema = get_output_schema(schema_agent) streamed_input: str | list[TResponseInputItem] = ( @@ -1618,6 +1681,8 @@ def run_streamed( streamed_result._state = run_state if run_state is not None: streamed_result._tool_use_tracker_snapshot = run_state.get_tool_use_tracker_snapshot() + if sandbox_runtime.enabled: + sandbox_runtime.apply_result_metadata(streamed_result) # Kick off the actual agent loop in the background and return the streamed result object. streamed_result.run_loop_task = asyncio.create_task( @@ -1636,8 +1701,11 @@ def run_streamed( session=session, run_state=run_state, is_resumed_state=is_resumed_state, + sandbox_runtime=sandbox_runtime, ) ) + if sandbox_runtime.enabled: + streamed_result.ensure_sandbox_cleanup_on_completion() return streamed_result diff --git a/src/agents/run_config.py b/src/agents/run_config.py index ad21f6c3b9..c3a6f13df7 100644 --- a/src/agents/run_config.py +++ b/src/agents/run_config.py @@ -22,6 +22,11 @@ if TYPE_CHECKING: from .agent import Agent from .run_context import RunContextWrapper + from .sandbox.manifest import Manifest + from .sandbox.session.base_sandbox_session import BaseSandboxSession + from .sandbox.session.sandbox_client import BaseSandboxClient + from .sandbox.session.sandbox_session_state import SandboxSessionState + from .sandbox.snapshot import SnapshotSpec DEFAULT_MAX_TURNS = 10 @@ -80,6 +85,29 @@ class ToolErrorFormatterArgs(Generic[TContext]): ToolErrorFormatter = Callable[[ToolErrorFormatterArgs[Any]], MaybeAwaitable[Optional[str]]] +@dataclass +class SandboxRunConfig: + """Grouped sandbox runtime configuration for `Runner`.""" + + client: BaseSandboxClient[Any] | None = None + """Sandbox client used to create or resume sandbox sessions.""" + + options: Any | None = None + """Sandbox-client-specific options used when creating a fresh session.""" + + session: BaseSandboxSession | None = None + """Live sandbox session override for the current process.""" + + session_state: SandboxSessionState | None = None + """Explicit sandbox session state to resume from when not using `RunState` payloads.""" + + manifest: Manifest | None = None + """Optional sandbox manifest override for fresh session creation.""" + + snapshot: SnapshotSpec | None = None + """Optional sandbox snapshot used for fresh session creation.""" + + @dataclass class RunConfig: """Configures settings for the entire agent run.""" @@ -191,6 +219,9 @@ class RunConfig: - ``"omit"`` strips reasoning item IDs from model input built by the runner. """ + sandbox: SandboxRunConfig | None = None + """Optional sandbox runtime configuration for `SandboxAgent` execution.""" + class RunOptions(TypedDict, Generic[TContext]): """Arguments for ``AgentRunner`` methods.""" @@ -231,6 +262,7 @@ class RunOptions(TypedDict, Generic[TContext]): "ReasoningItemIdPolicy", "RunConfig", "RunOptions", + "SandboxRunConfig", "ToolErrorFormatter", "ToolErrorFormatterArgs", "_default_trace_include_sensitive_data", diff --git a/src/agents/run_internal/agent_bindings.py b/src/agents/run_internal/agent_bindings.py new file mode 100644 index 0000000000..93e3702b14 --- /dev/null +++ b/src/agents/run_internal/agent_bindings.py @@ -0,0 +1,38 @@ +from __future__ import annotations + +from dataclasses import dataclass +from typing import Generic + +from ..agent import Agent +from ..run_context import TContext + +__all__ = [ + "AgentBindings", + "bind_execution_agent", + "bind_public_agent", +] + + +@dataclass(frozen=True) +class AgentBindings(Generic[TContext]): + """Carry the public and execution agent identities for a turn.""" + + public_agent: Agent[TContext] + execution_agent: Agent[TContext] + + +def bind_public_agent(agent: Agent[TContext]) -> AgentBindings[TContext]: + """Build bindings for non-rewritten execution where both identities are the same.""" + return AgentBindings(public_agent=agent, execution_agent=agent) + + +def bind_execution_agent( + *, + public_agent: Agent[TContext], + execution_agent: Agent[TContext], +) -> AgentBindings[TContext]: + """Build bindings for execution-only clones such as sandbox-prepared agents.""" + return AgentBindings( + public_agent=public_agent, + execution_agent=execution_agent, + ) diff --git a/src/agents/run_internal/agent_runner_helpers.py b/src/agents/run_internal/agent_runner_helpers.py index 776e406703..3b51d616ea 100644 --- a/src/agents/run_internal/agent_runner_helpers.py +++ b/src/agents/run_internal/agent_runner_helpers.py @@ -253,6 +253,11 @@ def build_interruption_result( original_input: str | list[TResponseInputItem], ) -> RunResult: """Create a RunResult for an interruption path.""" + identity_root_agent = ( + run_state._starting_agent + if run_state is not None and run_state._starting_agent is not None + else current_agent + ) result = RunResult( input=result_input, new_items=session_items, @@ -266,7 +271,10 @@ def build_interruption_result( context_wrapper=context_wrapper, interruptions=interruptions, _last_processed_response=processed_response, - _tool_use_tracker_snapshot=serialize_tool_use_tracker(tool_use_tracker), + _tool_use_tracker_snapshot=serialize_tool_use_tracker( + tool_use_tracker, + starting_agent=identity_root_agent, + ), max_turns=max_turns, ) result._current_turn = current_turn diff --git a/src/agents/run_internal/run_loop.py b/src/agents/run_internal/run_loop.py index 3d21d89fda..4d82835cb5 100644 --- a/src/agents/run_internal/run_loop.py +++ b/src/agents/run_internal/run_loop.py @@ -57,6 +57,7 @@ from ..run_context import AgentHookContext, RunContextWrapper, TContext from ..run_error_handlers import RunErrorHandlers from ..run_state import RunState +from ..sandbox.runtime import SandboxRuntime from ..stream_events import ( AgentUpdatedStreamEvent, RawResponsesStreamEvent, @@ -68,6 +69,7 @@ from ..tracing.span_data import AgentSpanData from ..usage import Usage from ..util import _coro, _error_tracing +from .agent_bindings import AgentBindings, bind_public_agent from .agent_runner_helpers import apply_resumed_conversation_settings from .approvals import approvals_from_step from .error_handlers import ( @@ -413,6 +415,7 @@ async def start_streaming( run_state: RunState[TContext] | None = None, *, is_resumed_state: bool = False, + sandbox_runtime: SandboxRuntime[TContext] | None = None, ): """Run the streaming loop for a run result.""" if streamed_result.trace: @@ -598,6 +601,25 @@ async def _save_stream_items_without_count( try: while True: + current_bindings = bind_public_agent(current_agent) + execution_agent = current_bindings.execution_agent + prepared_turn_input = copy_input_items(streamed_result.input) + if sandbox_runtime is not None: + prepared_sandbox = await sandbox_runtime.prepare_agent( + current_agent=current_agent, + current_input=prepared_turn_input, + context_wrapper=context_wrapper, + is_resumed_state=is_resumed_state, + ) + current_bindings = prepared_sandbox.bindings + execution_agent = current_bindings.execution_agent + prepared_turn_input = copy_input_items(prepared_sandbox.input) + streamed_result.input = prepared_turn_input + streamed_result._original_input = copy_input_items(prepared_turn_input) + if run_state is not None: + run_state._original_input = copy_input_items(prepared_turn_input) + sandbox_runtime.apply_result_metadata(streamed_result) + if is_resumed_state and run_state is not None and run_state._current_step is not None: if isinstance(run_state._current_step, NextStepInterruption): if not run_state._model_responses or not run_state._last_processed_response: @@ -606,7 +628,7 @@ async def _save_stream_items_without_count( last_model_response = run_state._model_responses[-1] turn_result = await resolve_interrupted_turn( - agent=current_agent, + bindings=current_bindings, original_input=run_state._original_input, original_pre_step_items=run_state._generated_items, new_response=last_model_response, @@ -621,7 +643,12 @@ async def _save_stream_items_without_count( current_agent, run_state._last_processed_response ) streamed_result._tool_use_tracker_snapshot = serialize_tool_use_tracker( - tool_use_tracker + tool_use_tracker, + starting_agent=( + run_state._starting_agent + if run_state is not None and run_state._starting_agent is not None + else starting_agent + ), ) streamed_result.input = turn_result.original_input @@ -712,14 +739,14 @@ async def _save_stream_items_without_count( if streamed_result.is_complete: break - all_tools = await get_all_tools(current_agent, context_wrapper) + all_tools = await get_all_tools(execution_agent, context_wrapper) await initialize_computer_tools(tools=all_tools, context_wrapper=context_wrapper) if current_span is None: handoff_names = [ - h.agent_name for h in await get_handoffs(current_agent, context_wrapper) + h.agent_name for h in await get_handoffs(execution_agent, context_wrapper) ] - if output_schema := get_output_schema(current_agent): + if output_schema := get_output_schema(execution_agent): output_type_name = output_schema.name() else: output_type_name = "str" @@ -831,7 +858,7 @@ async def _save_stream_items_without_count( await run_input_guardrails_with_queue( starting_agent, sequential_guardrails, - ItemHelpers.input_to_new_input_list(prepared_input), + ItemHelpers.input_to_new_input_list(prepared_turn_input), context_wrapper, streamed_result, current_span, @@ -858,7 +885,7 @@ async def _save_stream_items_without_count( run_input_guardrails_with_queue( starting_agent, parallel_guardrails, - ItemHelpers.input_to_new_input_list(prepared_input), + ItemHelpers.input_to_new_input_list(prepared_turn_input), context_wrapper, streamed_result, current_span, @@ -882,7 +909,7 @@ async def _save_stream_items_without_count( ) turn_result = await run_single_turn_streamed( streamed_result, - current_agent, + current_bindings, hooks, context_wrapper, run_config, @@ -906,7 +933,12 @@ async def _save_stream_items_without_count( ) should_run_agent_start_hooks = False streamed_result._tool_use_tracker_snapshot = serialize_tool_use_tracker( - tool_use_tracker + tool_use_tracker, + starting_agent=( + run_state._starting_agent + if run_state is not None and run_state._starting_agent is not None + else starting_agent + ), ) streamed_result.raw_responses = streamed_result.raw_responses + [ @@ -1086,7 +1118,7 @@ async def _save_stream_items_without_count( async def run_single_turn_streamed( streamed_result: RunResultStreaming, - agent: Agent[TContext], + bindings: AgentBindings[TContext], hooks: RunHooks[TContext], context_wrapper: RunContextWrapper[TContext], run_config: RunConfig, @@ -1100,6 +1132,8 @@ async def run_single_turn_streamed( reasoning_item_id_policy: ReasoningItemIdPolicy | None = None, ) -> SingleStepResult: """Run a single streamed turn and emit events as results arrive.""" + public_agent = bindings.public_agent + execution_agent = bindings.execution_agent emitted_tool_call_ids: set[str] = set() emitted_reasoning_item_ids: set[str] = set() emitted_tool_search_fingerprints: set[str] = set() @@ -1145,28 +1179,28 @@ def _tool_search_fingerprint(raw_item: Any) -> str: turn_input=turn_input, ) await asyncio.gather( - hooks.on_agent_start(agent_hook_context, agent), + hooks.on_agent_start(agent_hook_context, public_agent), ( - agent.hooks.on_start(agent_hook_context, agent) - if agent.hooks + public_agent.hooks.on_start(agent_hook_context, public_agent) + if public_agent.hooks else _coro.noop_coroutine() ), ) - output_schema = get_output_schema(agent) + output_schema = get_output_schema(execution_agent) - streamed_result.current_agent = agent - streamed_result._current_agent_output_schema = output_schema + streamed_result.current_agent = public_agent + streamed_result._current_agent_output_schema = get_output_schema(public_agent) system_prompt, prompt_config = await asyncio.gather( - agent.get_system_prompt(context_wrapper), - agent.get_prompt(context_wrapper), + execution_agent.get_system_prompt(context_wrapper), + execution_agent.get_prompt(context_wrapper), ) - handoffs = await get_handoffs(agent, context_wrapper) - model = get_model(agent, run_config) - model_settings = agent.model_settings.resolve(run_config.model_settings) - model_settings = maybe_reset_tool_choice(agent, tool_use_tracker, model_settings) + handoffs = await get_handoffs(execution_agent, context_wrapper) + model = get_model(execution_agent, run_config) + model_settings = execution_agent.model_settings.resolve(run_config.model_settings) + model_settings = maybe_reset_tool_choice(public_agent, tool_use_tracker, model_settings) final_response: ModelResponse | None = None @@ -1190,7 +1224,7 @@ def _tool_search_fingerprint(raw_item: Any) -> str: ) filtered = await maybe_filter_model_input( - agent=agent, + agent=public_agent, run_config=run_config, context_wrapper=context_wrapper, input_items=input, @@ -1214,10 +1248,15 @@ def _tool_search_fingerprint(raw_item: Any) -> str: raise RuntimeError("Prepared model input is empty") await asyncio.gather( - hooks.on_llm_start(context_wrapper, agent, filtered.instructions, filtered.input), + hooks.on_llm_start(context_wrapper, public_agent, filtered.instructions, filtered.input), ( - agent.hooks.on_llm_start(context_wrapper, agent, filtered.instructions, filtered.input) - if agent.hooks + public_agent.hooks.on_llm_start( + context_wrapper, + public_agent, + filtered.instructions, + filtered.input, + ) + if public_agent.hooks else _coro.noop_coroutine() ), ) @@ -1327,7 +1366,7 @@ async def rewind_model_request() -> None: RunItemStreamEvent( item=ToolSearchCallItem( raw_item=coerce_tool_search_call_raw_item(output_item), - agent=agent, + agent=public_agent, ), name="tool_search_called", ) @@ -1339,7 +1378,7 @@ async def rewind_model_request() -> None: RunItemStreamEvent( item=ToolSearchOutputItem( raw_item=coerce_tool_search_output_raw_item(output_item), - agent=agent, + agent=public_agent, ), name="tool_search_output_created", ) @@ -1381,7 +1420,7 @@ async def rewind_model_request() -> None: tool_item = ToolCallItem( raw_item=cast(ToolCallItemTypes, output_item), - agent=agent, + agent=public_agent, description=tool_description, title=tool_title, ) @@ -1395,7 +1434,7 @@ async def rewind_model_request() -> None: if reasoning_id and reasoning_id not in emitted_reasoning_item_ids: emitted_reasoning_item_ids.add(reasoning_id) - reasoning_item = ReasoningItem(raw_item=output_item, agent=agent) + reasoning_item = ReasoningItem(raw_item=output_item, agent=public_agent) streamed_result._event_queue.put_nowait( RunItemStreamEvent(item=reasoning_item, name="reasoning_item_created") ) @@ -1404,11 +1443,11 @@ async def rewind_model_request() -> None: context_wrapper.usage.add(final_response.usage) await asyncio.gather( ( - agent.hooks.on_llm_end(context_wrapper, agent, final_response) - if agent.hooks + public_agent.hooks.on_llm_end(context_wrapper, public_agent, final_response) + if public_agent.hooks else _coro.noop_coroutine() ), - hooks.on_llm_end(context_wrapper, agent, final_response), + hooks.on_llm_end(context_wrapper, public_agent, final_response), ) if not final_response: @@ -1421,7 +1460,7 @@ async def rewind_model_request() -> None: server_conversation_tracker.track_server_items(final_response) single_step_result = await get_single_step_result_from_response( - agent=agent, + bindings=bindings, original_input=streamed_result.input, pre_step_items=streamed_result._model_input_items, new_response=final_response, @@ -1480,7 +1519,7 @@ async def rewind_model_request() -> None: async def run_single_turn( *, - agent: Agent[TContext], + bindings: AgentBindings[TContext], all_tools: list[Tool], original_input: str | list[TResponseInputItem], generated_items: list[RunItem], @@ -1495,6 +1534,8 @@ async def run_single_turn( reasoning_item_id_policy: ReasoningItemIdPolicy | None = None, ) -> SingleStepResult: """Run a single non-streaming turn of the agent loop.""" + public_agent = bindings.public_agent + execution_agent = bindings.execution_agent try: turn_input = ItemHelpers.input_to_new_input_list(original_input) except Exception: @@ -1509,28 +1550,28 @@ async def run_single_turn( turn_input=turn_input, ) await asyncio.gather( - hooks.on_agent_start(agent_hook_context, agent), + hooks.on_agent_start(agent_hook_context, public_agent), ( - agent.hooks.on_start(agent_hook_context, agent) - if agent.hooks + public_agent.hooks.on_start(agent_hook_context, public_agent) + if public_agent.hooks else _coro.noop_coroutine() ), ) system_prompt, prompt_config = await asyncio.gather( - agent.get_system_prompt(context_wrapper), - agent.get_prompt(context_wrapper), + execution_agent.get_system_prompt(context_wrapper), + execution_agent.get_prompt(context_wrapper), ) - output_schema = get_output_schema(agent) - handoffs = await get_handoffs(agent, context_wrapper) + output_schema = get_output_schema(execution_agent) + handoffs = await get_handoffs(execution_agent, context_wrapper) if server_conversation_tracker is not None: input = server_conversation_tracker.prepare_input(original_input, generated_items) else: input = _prepare_turn_input_items(original_input, generated_items, reasoning_item_id_policy) new_response = await get_new_response( - agent, + bindings, system_prompt, input, output_schema, @@ -1547,7 +1588,7 @@ async def run_single_turn( ) return await get_single_step_result_from_response( - agent=agent, + bindings=bindings, original_input=original_input, pre_step_items=generated_items, new_response=new_response, @@ -1562,7 +1603,7 @@ async def run_single_turn( async def get_new_response( - agent: Agent[TContext], + bindings: AgentBindings[TContext], system_prompt: str | None, input: list[TResponseInputItem], output_schema: AgentOutputSchemaBase | None, @@ -1578,8 +1619,10 @@ async def get_new_response( session_items_to_rewind: list[TResponseInputItem] | None = None, ) -> ModelResponse: """Call the model and return the raw response, handling retries and hooks.""" + public_agent = bindings.public_agent + execution_agent = bindings.execution_agent filtered = await maybe_filter_model_input( - agent=agent, + agent=public_agent, run_config=run_config, context_wrapper=context_wrapper, input_items=input, @@ -1588,23 +1631,23 @@ async def get_new_response( if isinstance(filtered.input, list): filtered.input = deduplicate_input_items_preferring_latest(filtered.input) - model = get_model(agent, run_config) - model_settings = agent.model_settings.resolve(run_config.model_settings) - model_settings = maybe_reset_tool_choice(agent, tool_use_tracker, model_settings) + model = get_model(execution_agent, run_config) + model_settings = execution_agent.model_settings.resolve(run_config.model_settings) + model_settings = maybe_reset_tool_choice(public_agent, tool_use_tracker, model_settings) if server_conversation_tracker is not None: server_conversation_tracker.mark_input_as_sent(filtered.input) await asyncio.gather( - hooks.on_llm_start(context_wrapper, agent, filtered.instructions, filtered.input), + hooks.on_llm_start(context_wrapper, public_agent, filtered.instructions, filtered.input), ( - agent.hooks.on_llm_start( + public_agent.hooks.on_llm_start( context_wrapper, - agent, + public_agent, filtered.instructions, filtered.input, ) - if agent.hooks + if public_agent.hooks else _coro.noop_coroutine() ), ) @@ -1660,11 +1703,11 @@ async def rewind_model_request() -> None: await asyncio.gather( ( - agent.hooks.on_llm_end(context_wrapper, agent, new_response) - if agent.hooks + public_agent.hooks.on_llm_end(context_wrapper, public_agent, new_response) + if public_agent.hooks else _coro.noop_coroutine() ), - hooks.on_llm_end(context_wrapper, agent, new_response), + hooks.on_llm_end(context_wrapper, public_agent, new_response), ) return new_response diff --git a/src/agents/run_internal/tool_execution.py b/src/agents/run_internal/tool_execution.py index 4511045288..f5d56d2dfc 100644 --- a/src/agents/run_internal/tool_execution.py +++ b/src/agents/run_internal/tool_execution.py @@ -87,6 +87,7 @@ from ..util._approvals import evaluate_needs_approval_setting from ..util._types import MaybeAwaitable from ._asyncio_progress import get_function_tool_task_progress_deadline +from .agent_bindings import AgentBindings, bind_public_agent from .approvals import append_approval_error_output from .items import ( REJECTION_MESSAGE, @@ -1279,14 +1280,15 @@ class _FunctionToolBatchExecutor: def __init__( self, *, - agent: Agent[Any], + bindings: AgentBindings[Any], tool_runs: list[ToolRunFunction], hooks: RunHooks[Any], context_wrapper: RunContextWrapper[Any], config: RunConfig, isolate_parallel_failures: bool | None, ) -> None: - self.agent = agent + self.execution_agent = bindings.execution_agent + self.public_agent = bindings.public_agent self.tool_runs = tool_runs self.hooks = hooks self.context_wrapper = context_wrapper @@ -1310,7 +1312,7 @@ async def execute( list[FunctionToolResult], list[ToolInputGuardrailResult], list[ToolOutputGuardrailResult] ]: self.available_function_tools = await resolve_enabled_function_tools( - self.agent, + self.execution_agent, self.context_wrapper, ) for tool_run in self.tool_runs: @@ -1457,10 +1459,10 @@ async def _run_single_tool( tool_call.call_id, tool_call=raw_tool_call, tool_namespace=tool_context_namespace, - agent=self.agent, + agent=self.public_agent, run_config=self.config, ) - agent_hooks = self.agent.hooks + agent_hooks = self.public_agent.hooks if self.config.trace_include_sensitive_data: span_fn.span_data.input = tool_call.arguments @@ -1526,7 +1528,7 @@ async def _maybe_execute_tool_approval( ) if approval_status is None: approval_item = ToolApprovalItem( - agent=self.agent, + agent=self.public_agent, raw_item=raw_tool_call, tool_name=func_tool.name, tool_namespace=tool_namespace, @@ -1566,7 +1568,7 @@ async def _maybe_execute_tool_approval( tool=func_tool, output=rejection_message, run_item=function_rejection_item( - self.agent, + self.public_agent, tool_call, rejection_message=rejection_message, scope_id=self.tool_state_scope_id, @@ -1585,16 +1587,16 @@ async def _execute_single_tool_body( rejected_message = await _execute_tool_input_guardrails( func_tool=func_tool, tool_context=tool_context, - agent=self.agent, + agent=self.public_agent, tool_input_guardrail_results=self.tool_input_guardrail_results, ) if rejected_message is not None: return rejected_message await asyncio.gather( - self.hooks.on_tool_start(tool_context, self.agent, func_tool), + self.hooks.on_tool_start(tool_context, self.public_agent, func_tool), ( - agent_hooks.on_tool_start(tool_context, self.agent, func_tool) + agent_hooks.on_tool_start(tool_context, self.public_agent, func_tool) if agent_hooks else _coro.noop_coroutine() ), @@ -1654,15 +1656,15 @@ async def _invoke_tool_and_run_post_invoke( final_result = await _execute_tool_output_guardrails( func_tool=func_tool, tool_context=tool_context, - agent=self.agent, + agent=self.public_agent, real_result=real_result, tool_output_guardrail_results=self.tool_output_guardrail_results, ) await asyncio.gather( - self.hooks.on_tool_end(tool_context, self.agent, func_tool, final_result), + self.hooks.on_tool_end(tool_context, self.public_agent, func_tool, final_result), ( - agent_hooks.on_tool_end(tool_context, self.agent, func_tool, final_result) + agent_hooks.on_tool_end(tool_context, self.public_agent, func_tool, final_result) if agent_hooks else _coro.noop_coroutine() ), @@ -1763,7 +1765,7 @@ def _build_function_tool_results(self) -> list[FunctionToolResult]: run_item = ToolCallOutputItem( output=result, raw_item=ItemHelpers.tool_call_output_item(tool_run.tool_call, result), - agent=self.agent, + agent=self.public_agent, ) else: # Skip tool output until nested interruptions are resolved. @@ -1784,7 +1786,7 @@ def _build_function_tool_results(self) -> list[FunctionToolResult]: async def execute_function_tool_calls( *, - agent: Agent[Any], + bindings: AgentBindings[Any], tool_runs: list[ToolRunFunction], hooks: RunHooks[Any], context_wrapper: RunContextWrapper[Any], @@ -1795,7 +1797,7 @@ async def execute_function_tool_calls( ]: """Execute function tool calls with approvals, guardrails, and hooks.""" return await _FunctionToolBatchExecutor( - agent=agent, + bindings=bindings, tool_runs=tool_runs, hooks=hooks, context_wrapper=context_wrapper, @@ -1806,7 +1808,7 @@ async def execute_function_tool_calls( async def execute_local_shell_calls( *, - agent: Agent[Any], + public_agent: Agent[Any], calls: list[ToolRunLocalShellCall], context_wrapper: RunContextWrapper[Any], hooks: RunHooks[Any], @@ -1819,7 +1821,7 @@ async def execute_local_shell_calls( for call in calls: results.append( await LocalShellAction.execute( - agent=agent, + agent=public_agent, call=call, hooks=hooks, context_wrapper=context_wrapper, @@ -1831,7 +1833,7 @@ async def execute_local_shell_calls( async def execute_shell_calls( *, - agent: Agent[Any], + public_agent: Agent[Any], calls: list[ToolRunShellCall], context_wrapper: RunContextWrapper[Any], hooks: RunHooks[Any], @@ -1844,7 +1846,7 @@ async def execute_shell_calls( for call in calls: results.append( await ShellAction.execute( - agent=agent, + agent=public_agent, call=call, hooks=hooks, context_wrapper=context_wrapper, @@ -1856,7 +1858,7 @@ async def execute_shell_calls( async def execute_apply_patch_calls( *, - agent: Agent[Any], + public_agent: Agent[Any], calls: list[ToolRunApplyPatchCall], context_wrapper: RunContextWrapper[Any], hooks: RunHooks[Any], @@ -1869,7 +1871,7 @@ async def execute_apply_patch_calls( for call in calls: results.append( await ApplyPatchAction.execute( - agent=agent, + agent=public_agent, call=call, hooks=hooks, context_wrapper=context_wrapper, @@ -1881,7 +1883,7 @@ async def execute_apply_patch_calls( async def execute_computer_actions( *, - agent: Agent[Any], + public_agent: Agent[Any], actions: list[ToolRunComputerAction], hooks: RunHooks[Any], context_wrapper: RunContextWrapper[Any], @@ -1898,7 +1900,7 @@ async def execute_computer_actions( for check in action.tool_call.pending_safety_checks: data = ComputerToolSafetyCheckData( ctx_wrapper=context_wrapper, - agent=agent, + agent=public_agent, tool_call=action.tool_call, safety_check=check, ) @@ -1917,7 +1919,7 @@ async def execute_computer_actions( results.append( await ComputerAction.execute( - agent=agent, + agent=public_agent, action=action, hooks=hooks, context_wrapper=context_wrapper, @@ -2081,7 +2083,7 @@ async def _resolve_tool_run( if tool_runs: function_results, _, _ = await execute_function_tool_calls( - agent=agent, + bindings=bind_public_agent(agent), tool_runs=tool_runs, hooks=hooks, context_wrapper=context_wrapper, diff --git a/src/agents/run_internal/tool_planning.py b/src/agents/run_internal/tool_planning.py index dabb83b4ac..d56bc03a26 100644 --- a/src/agents/run_internal/tool_planning.py +++ b/src/agents/run_internal/tool_planning.py @@ -24,6 +24,7 @@ from ..run_context import RunContextWrapper from ..tool import FunctionTool, MCPToolApprovalRequest from ..tool_guardrails import ToolInputGuardrailResult, ToolOutputGuardrailResult +from .agent_bindings import AgentBindings from .run_steps import ( ToolRunApplyPatchCall, ToolRunComputerAction, @@ -518,7 +519,7 @@ async def _select_function_tool_runs_for_resume( async def _execute_tool_plan( *, plan: ToolExecutionPlan, - agent: Agent[Any], + bindings: AgentBindings[Any], hooks, context_wrapper: RunContextWrapper[Any], run_config, @@ -533,6 +534,7 @@ async def _execute_tool_plan( list[RunItem], ]: """Execute tool runs captured in a ToolExecutionPlan.""" + public_agent = bindings.public_agent isolate_function_tool_failures = len(plan.function_runs) > 1 or ( parallel and ( @@ -551,7 +553,7 @@ async def _execute_tool_plan( local_shell_results, ) = await asyncio.gather( execute_function_tool_calls( - agent=agent, + bindings=bindings, tool_runs=plan.function_runs, hooks=hooks, context_wrapper=context_wrapper, @@ -559,28 +561,28 @@ async def _execute_tool_plan( isolate_parallel_failures=isolate_function_tool_failures, ), execute_computer_actions( - agent=agent, + public_agent=public_agent, actions=plan.computer_actions, hooks=hooks, context_wrapper=context_wrapper, config=run_config, ), execute_shell_calls( - agent=agent, + public_agent=public_agent, calls=plan.shell_calls, hooks=hooks, context_wrapper=context_wrapper, config=run_config, ), execute_apply_patch_calls( - agent=agent, + public_agent=public_agent, calls=plan.apply_patch_calls, hooks=hooks, context_wrapper=context_wrapper, config=run_config, ), execute_local_shell_calls( - agent=agent, + public_agent=public_agent, calls=plan.local_shell_calls, hooks=hooks, context_wrapper=context_wrapper, @@ -593,7 +595,7 @@ async def _execute_tool_plan( tool_input_guardrail_results, tool_output_guardrail_results, ) = await execute_function_tool_calls( - agent=agent, + bindings=bindings, tool_runs=plan.function_runs, hooks=hooks, context_wrapper=context_wrapper, @@ -601,28 +603,28 @@ async def _execute_tool_plan( isolate_parallel_failures=isolate_function_tool_failures, ) computer_results = await execute_computer_actions( - agent=agent, + public_agent=public_agent, actions=plan.computer_actions, hooks=hooks, context_wrapper=context_wrapper, config=run_config, ) shell_results = await execute_shell_calls( - agent=agent, + public_agent=public_agent, calls=plan.shell_calls, hooks=hooks, context_wrapper=context_wrapper, config=run_config, ) apply_patch_results = await execute_apply_patch_calls( - agent=agent, + public_agent=public_agent, calls=plan.apply_patch_calls, hooks=hooks, context_wrapper=context_wrapper, config=run_config, ) local_shell_results = await execute_local_shell_calls( - agent=agent, + public_agent=public_agent, calls=plan.local_shell_calls, hooks=hooks, context_wrapper=context_wrapper, diff --git a/src/agents/run_internal/tool_use_tracker.py b/src/agents/run_internal/tool_use_tracker.py index e763f175a7..60ff9a1731 100644 --- a/src/agents/run_internal/tool_use_tracker.py +++ b/src/agents/run_internal/tool_use_tracker.py @@ -17,7 +17,11 @@ ToolSearchCallItem, ToolSearchOutputItem, ) -from ..run_state import _build_agent_map +from ..run_state import ( + _build_agent_identity_keys_by_id, + _build_agent_identity_map, + _build_agent_map, +) from .run_steps import ProcessedResponse, ToolRunFunction __all__ = [ @@ -112,11 +116,23 @@ def from_serializable(cls, data: dict[str, list[str]]) -> AgentToolUseTracker: return tracker -def serialize_tool_use_tracker(tool_use_tracker: AgentToolUseTracker) -> dict[str, list[str]]: +def serialize_tool_use_tracker( + tool_use_tracker: AgentToolUseTracker, + *, + starting_agent: Agent[Any] | None = None, +) -> dict[str, list[str]]: """Convert the AgentToolUseTracker into a serializable snapshot.""" + agent_identity_keys_by_id = ( + _build_agent_identity_keys_by_id(starting_agent) if starting_agent is not None else None + ) snapshot: dict[str, list[str]] = {} for agent, tool_names in tool_use_tracker.agent_to_tools: - snapshot[agent.name] = list(tool_names) + agent_key = None + if agent_identity_keys_by_id is not None: + agent_key = agent_identity_keys_by_id.get(id(agent)) + if agent_key is None: + agent_key = getattr(agent, "name", agent.__class__.__name__) + snapshot.setdefault(agent_key, []).extend(tool_names) return snapshot @@ -131,8 +147,9 @@ def hydrate_tool_use_tracker( return agent_map = _build_agent_map(starting_agent) + agent_identity_map = _build_agent_identity_map(starting_agent) for agent_name, tool_names in snapshot.items(): - agent = agent_map.get(agent_name) + agent = agent_identity_map.get(agent_name) or agent_map.get(agent_name) if agent is None: continue tool_use_tracker.add_tool_use(agent, list(tool_names)) diff --git a/src/agents/run_internal/turn_resolution.py b/src/agents/run_internal/turn_resolution.py index 2b3f98b55b..df035cbfb9 100644 --- a/src/agents/run_internal/turn_resolution.py +++ b/src/agents/run_internal/turn_resolution.py @@ -84,6 +84,7 @@ from ..tracing import SpanError, handoff_span from ..util import _coro, _error_tracing from ..util._approvals import evaluate_needs_approval_setting +from .agent_bindings import AgentBindings from .items import ( REJECTION_MESSAGE, apply_patch_rejection_item, @@ -155,7 +156,7 @@ async def _maybe_finalize_from_tool_results( *, - agent: Agent[TContext], + public_agent: Agent[TContext], original_input: str | list[TResponseInputItem], new_response: ModelResponse, pre_step_items: list[RunItem], @@ -167,12 +168,12 @@ async def _maybe_finalize_from_tool_results( tool_output_guardrail_results: list[ToolOutputGuardrailResult], ) -> SingleStepResult | None: check_tool_use = await check_for_final_output_from_tools( - agent, function_results, context_wrapper + public_agent, function_results, context_wrapper ) if not check_tool_use.is_final_output: return None - if not agent.output_type or agent.output_type is str: + if not public_agent.output_type or public_agent.output_type is str: check_tool_use.final_output = str(check_tool_use.final_output) if check_tool_use.final_output is None: @@ -182,7 +183,7 @@ async def _maybe_finalize_from_tool_results( ) return await execute_final_output( - agent=agent, + public_agent=public_agent, original_input=original_input, new_response=new_response, pre_step_items=pre_step_items, @@ -218,7 +219,7 @@ async def run_final_output_hooks( async def execute_final_output_step( *, - agent: Agent[Any], + public_agent: Agent[Any], original_input: str | list[TResponseInputItem], new_response: ModelResponse, pre_step_items: list[RunItem], @@ -235,7 +236,7 @@ async def execute_final_output_step( ) -> SingleStepResult: """Finalize a turn once final output is known and run end hooks.""" final_output_hooks = run_final_output_hooks_fn or run_final_output_hooks - await final_output_hooks(agent, hooks, context_wrapper, final_output) + await final_output_hooks(public_agent, hooks, context_wrapper, final_output) return SingleStepResult( original_input=original_input, @@ -251,7 +252,7 @@ async def execute_final_output_step( async def execute_final_output( *, - agent: Agent[Any], + public_agent: Agent[Any], original_input: str | list[TResponseInputItem], new_response: ModelResponse, pre_step_items: list[RunItem], @@ -268,7 +269,7 @@ async def execute_final_output( ) -> SingleStepResult: """Convenience wrapper to finalize a turn and run end hooks.""" return await execute_final_output_step( - agent=agent, + public_agent=public_agent, original_input=original_input, new_response=new_response, pre_step_items=pre_step_items, @@ -284,7 +285,7 @@ async def execute_final_output( async def execute_handoffs( *, - agent: Agent[TContext], + public_agent: Agent[TContext], original_input: str | list[TResponseInputItem], pre_step_items: list[RunItem], new_step_items: list[RunItem], @@ -310,14 +311,14 @@ def nest_history(data: HandoffInputData, mapper: Any | None = None) -> HandoffIn ToolCallOutputItem( output=output_message, raw_item=ItemHelpers.tool_call_output_item(handoff.tool_call, output_message), - agent=agent, + agent=public_agent, ) for handoff in run_handoffs[1:] ] ) actual_handoff = run_handoffs[0] - with handoff_span(from_agent=agent.name) as span_handoff: + with handoff_span(from_agent=public_agent.name) as span_handoff: handoff = actual_handoff.handoff new_agent: Agent[Any] = await handoff.on_invoke_handoff( context_wrapper, actual_handoff.tool_call.arguments @@ -336,12 +337,12 @@ def nest_history(data: HandoffInputData, mapper: Any | None = None) -> HandoffIn new_step_items.append( HandoffOutputItem( - agent=agent, + agent=public_agent, raw_item=ItemHelpers.tool_call_output_item( actual_handoff.tool_call, handoff.get_transfer_message(new_agent), ), - source_agent=agent, + source_agent=public_agent, target_agent=new_agent, ) ) @@ -349,16 +350,16 @@ def nest_history(data: HandoffInputData, mapper: Any | None = None) -> HandoffIn await asyncio.gather( hooks.on_handoff( context=context_wrapper, - from_agent=agent, + from_agent=public_agent, to_agent=new_agent, ), ( - agent.hooks.on_handoff( + public_agent.hooks.on_handoff( context_wrapper, agent=new_agent, - source=agent, + source=public_agent, ) - if agent.hooks + if public_agent.hooks else _coro.noop_coroutine() ), ) @@ -386,7 +387,7 @@ def nest_history(data: HandoffInputData, mapper: Any | None = None) -> HandoffIn if input_filter and handoff_input_data is not None: filter_name = getattr(input_filter, "__qualname__", repr(input_filter)) - from_agent = getattr(agent, "name", agent.__class__.__name__) + from_agent = getattr(public_agent, "name", public_agent.__class__.__name__) to_agent = getattr(new_agent, "name", new_agent.__class__.__name__) logger.debug( "Filtering handoff inputs with %s for %s -> %s", @@ -498,7 +499,7 @@ async def check_for_final_output_from_tools( async def execute_tools_and_side_effects( *, - agent: Agent[TContext], + bindings: AgentBindings[TContext], original_input: str | list[TResponseInputItem], pre_step_items: list[RunItem], new_response: ModelResponse, @@ -509,6 +510,7 @@ async def execute_tools_and_side_effects( run_config: RunConfig, ) -> SingleStepResult: """Run one turn of the loop, coordinating tools, approvals, guardrails, and handoffs.""" + public_agent = bindings.public_agent execute_final_output_call = execute_final_output execute_handoffs_call = execute_handoffs @@ -518,7 +520,7 @@ async def execute_tools_and_side_effects( plan = _build_plan_for_fresh_turn( processed_response=processed_response, - agent=agent, + agent=public_agent, context_wrapper=context_wrapper, approval_items_by_call_id=approval_items_by_call_id, ) @@ -538,7 +540,7 @@ async def execute_tools_and_side_effects( local_shell_results, ) = await _execute_tool_plan( plan=plan, - agent=agent, + bindings=bindings, hooks=hooks, context_wrapper=context_wrapper, run_config=run_config, @@ -579,7 +581,7 @@ async def execute_tools_and_side_effects( ) await _append_mcp_callback_results( - agent=agent, + agent=public_agent, requests=plan.mcp_requests_with_callback, context_wrapper=context_wrapper, append_item=new_step_items.append, @@ -587,7 +589,7 @@ async def execute_tools_and_side_effects( if run_handoffs := processed_response.handoffs: return await execute_handoffs_call( - agent=agent, + public_agent=public_agent, original_input=original_input, pre_step_items=pre_step_items, new_step_items=new_step_items, @@ -599,7 +601,7 @@ async def execute_tools_and_side_effects( ) tool_final_output = await _maybe_finalize_from_tool_results( - agent=agent, + public_agent=public_agent, original_input=original_input, new_response=new_response, pre_step_items=pre_step_items, @@ -626,7 +628,7 @@ async def execute_tools_and_side_effects( if output_schema and not output_schema.is_plain_text() and potential_final_output_text: final_output = output_schema.validate_json(potential_final_output_text) return await execute_final_output_call( - agent=agent, + public_agent=public_agent, original_input=original_input, new_response=new_response, pre_step_items=pre_step_items, @@ -639,7 +641,7 @@ async def execute_tools_and_side_effects( ) if not output_schema or output_schema.is_plain_text(): return await execute_final_output_call( - agent=agent, + public_agent=public_agent, original_input=original_input, new_response=new_response, pre_step_items=pre_step_items, @@ -664,7 +666,7 @@ async def execute_tools_and_side_effects( async def resolve_interrupted_turn( *, - agent: Agent[TContext], + bindings: AgentBindings[TContext], original_input: str | list[TResponseInputItem], original_pre_step_items: list[RunItem], new_response: ModelResponse, @@ -676,6 +678,8 @@ async def resolve_interrupted_turn( nest_handoff_history_fn: Callable[..., HandoffInputData] | None = None, ) -> SingleStepResult: """Continue a turn that was previously interrupted waiting for tool approval.""" + public_agent = bindings.public_agent + execution_agent = bindings.execution_agent execute_handoffs_call = execute_handoffs @@ -719,7 +723,7 @@ async def _record_function_rejection( ) rejected_function_outputs.append( function_rejection_item( - agent, + public_agent, tool_call, rejection_message=rejection_message, scope_id=tool_state_scope_id, @@ -793,7 +797,7 @@ async def _build_shell_rejection(run: ToolRunShellCall, call_id: str) -> RunItem return cast( RunItem, shell_rejection_item( - agent, + public_agent, call_id, rejection_message=rejection_message, ), @@ -810,7 +814,7 @@ async def _build_apply_patch_rejection(run: ToolRunApplyPatchCall, call_id: str) return cast( RunItem, apply_patch_rejection_item( - agent, + public_agent, call_id, rejection_message=rejection_message, ), @@ -893,20 +897,39 @@ def _add_pending_interruption(item: ToolApprovalItem | None) -> None: pending_interruption_keys.add(key) pending_interruptions.append(item) + def _allow_legacy_name_agent_match() -> bool: + schema_version = getattr(run_state, "_schema_version", None) + if not isinstance(schema_version, str): + return False + try: + version_parts = tuple(int(part) for part in schema_version.split(".")) + except ValueError: + return False + # Schema 1.6 and earlier only serialized approval owners by agent name. With duplicate-name + # agents, deserialization can legitimately resolve the approval to a sibling instance, so + # resume must accept a same-name match for those legacy snapshots. Schema 1.7+ persists + # duplicate-name identities, so newer snapshots should continue requiring object identity. + return version_parts < (1, 7) + + allow_legacy_name_agent_match = _allow_legacy_name_agent_match() + def _approval_matches_agent(approval: ToolApprovalItem) -> bool: approval_agent = approval.agent if approval_agent is None: return False - if approval_agent is agent: + if approval_agent is public_agent: return True - return getattr(approval_agent, "name", None) == agent.name + return allow_legacy_name_agent_match and approval_agent.name == public_agent.name - available_function_tools = await resolve_enabled_function_tools(agent, context_wrapper) + available_function_tools = await resolve_enabled_function_tools( + execution_agent, + context_wrapper, + ) approval_rebuild_function_tools = available_function_tools - if pending_approval_items and agent.mcp_servers: + if pending_approval_items and execution_agent.mcp_servers: approval_rebuild_function_tools = [ tool - for tool in await agent.get_all_tools(context_wrapper) + for tool in await execution_agent.get_all_tools(context_wrapper) if isinstance(tool, FunctionTool) ] @@ -1030,7 +1053,7 @@ def _add_unmatched_pending(approval: ToolApprovalItem) -> None: record_rejection=_record_function_rejection, pending_interruption_adder=_add_pending_interruption, pending_item_builder=lambda run: ToolApprovalItem( - agent=agent, + agent=public_agent, raw_item=run.tool_call, tool_name=run.function_tool.name, tool_namespace=get_tool_call_namespace(run.tool_call), @@ -1071,7 +1094,7 @@ def _add_unmatched_pending(approval: ToolApprovalItem) -> None: rejection_builder=_build_shell_rejection, context_wrapper=context_wrapper, approval_items_by_call_id=approval_items_by_call_id, - agent=agent, + agent=public_agent, pending_interruption_adder=_add_pending_interruption, needs_approval_checker=_shell_needs_approval, output_exists_checker=_shell_output_exists, @@ -1084,7 +1107,7 @@ def _add_unmatched_pending(approval: ToolApprovalItem) -> None: rejection_builder=_build_apply_patch_rejection, context_wrapper=context_wrapper, approval_items_by_call_id=approval_items_by_call_id, - agent=agent, + agent=public_agent, pending_interruption_adder=_add_pending_interruption, needs_approval_checker=_apply_patch_needs_approval, output_exists_checker=_apply_patch_output_exists, @@ -1092,7 +1115,7 @@ def _add_unmatched_pending(approval: ToolApprovalItem) -> None: plan = _build_plan_for_resume_turn( processed_response=processed_response, - agent=agent, + agent=public_agent, context_wrapper=context_wrapper, approval_items_by_call_id=approval_items_by_call_id, pending_interruptions=pending_interruptions, @@ -1113,7 +1136,7 @@ def _add_unmatched_pending(approval: ToolApprovalItem) -> None: _local_shell_results, ) = await _execute_tool_plan( plan=plan, - agent=agent, + bindings=bindings, hooks=hooks, context_wrapper=context_wrapper, run_config=run_config, @@ -1164,7 +1187,7 @@ def _add_unmatched_pending(approval: ToolApprovalItem) -> None: ) await _append_mcp_callback_results( - agent=agent, + agent=public_agent, requests=plan.mcp_requests_with_callback, context_wrapper=context_wrapper, append_item=append_if_new, @@ -1177,7 +1200,7 @@ def _add_unmatched_pending(approval: ToolApprovalItem) -> None: original_pre_step_items=original_pre_step_items, mcp_approval_requests=processed_response.mcp_approval_requests, context_wrapper=context_wrapper, - agent=agent, + agent=public_agent, append_item=append_if_new, ) @@ -1232,7 +1255,7 @@ def _add_unmatched_pending(approval: ToolApprovalItem) -> None: if pending_handoffs: return await execute_handoffs_call( - agent=agent, + public_agent=public_agent, original_input=original_input, pre_step_items=pre_step_items, new_step_items=new_items, @@ -1245,7 +1268,7 @@ def _add_unmatched_pending(approval: ToolApprovalItem) -> None: ) tool_final_output = await _maybe_finalize_from_tool_results( - agent=agent, + public_agent=public_agent, original_input=original_input, new_response=new_response, pre_step_items=pre_step_items, @@ -1684,7 +1707,7 @@ def _dump_output_item(raw_item: Any) -> dict[str, Any]: async def get_single_step_result_from_response( *, - agent: Agent[TContext], + bindings: AgentBindings[TContext], all_tools: list[Tool], original_input: str | list[TResponseInputItem], pre_step_items: list[RunItem], @@ -1697,8 +1720,9 @@ async def get_single_step_result_from_response( tool_use_tracker, event_queue: asyncio.Queue[StreamEvent | QueueCompleteSentinel] | None = None, ) -> SingleStepResult: + item_agent = bindings.public_agent processed_response = process_model_response( - agent=agent, + agent=item_agent, all_tools=all_tools, response=new_response, output_schema=output_schema, @@ -1706,7 +1730,7 @@ async def get_single_step_result_from_response( existing_items=pre_step_items, ) - tool_use_tracker.record_processed_response(agent, processed_response) + tool_use_tracker.record_processed_response(item_agent, processed_response) if event_queue is not None and processed_response.new_items: handoff_items = [ @@ -1716,7 +1740,7 @@ async def get_single_step_result_from_response( stream_step_items_to_queue(cast(list[RunItem], handoff_items), event_queue) return await execute_tools_and_side_effects( - agent=agent, + bindings=bindings, original_input=original_input, pre_step_items=pre_step_items, new_response=new_response, diff --git a/src/agents/run_state.py b/src/agents/run_state.py index dcda9e073c..57e9cea104 100644 --- a/src/agents/run_state.py +++ b/src/agents/run_state.py @@ -2,12 +2,15 @@ from __future__ import annotations +import asyncio import copy import dataclasses import json +import threading from collections import deque -from collections.abc import Callable, Mapping, Sequence +from collections.abc import Callable, Iterator, Mapping, Sequence from dataclasses import dataclass, field +from pathlib import Path from typing import TYPE_CHECKING, Any, Generic, Literal, Optional, Union, cast from uuid import uuid4 @@ -42,6 +45,7 @@ get_function_tool_qualified_name, serialize_function_tool_lookup_key, ) +from .agent import Agent from .exceptions import UserError from .guardrail import ( GuardrailFunctionOutput, @@ -73,6 +77,7 @@ ) from .logger import logger from .run_context import RunContextWrapper +from .sandbox.session.base_sandbox_session import BaseSandboxSession from .tool import ( ApplyPatchTool, ComputerTool, @@ -96,7 +101,6 @@ from .util._json import _to_dump_compatible if TYPE_CHECKING: - from .agent import Agent from .guardrail import InputGuardrailResult, OutputGuardrailResult from .items import ModelResponse, RunItem from .run_internal.run_steps import ( @@ -118,10 +122,36 @@ # 3. to_json() always emits CURRENT_SCHEMA_VERSION. # 4. Forward compatibility is intentionally fail-fast (older SDKs reject newer or unsupported # versions). -CURRENT_SCHEMA_VERSION = "1.6" -SUPPORTED_SCHEMA_VERSIONS = frozenset( - {"1.0", "1.1", "1.2", "1.3", "1.4", "1.5", CURRENT_SCHEMA_VERSION} -) +CURRENT_SCHEMA_VERSION = "1.7" +# Keep this mapping in chronological order. Every schema bump must add a one-line summary here. +SCHEMA_VERSION_SUMMARIES: dict[str, str] = { + "1.0": "Initial RunState snapshot format for HITL pause/resume flows.", + "1.1": "Same payload as 1.0, but introduces explicit backward-read support policy.", + "1.2": "Persists reasoning_item_id_policy for resumed and streamed follow-up turns.", + "1.3": "Updates resumed trace semantics to reattach traces without duplicate starts.", + "1.4": "Stores request_id alongside each serialized model response.", + "1.5": "Renumbered unreleased baseline for tool-search snapshots and richer tool metadata.", + "1.6": "Persists explicit approval rejection messages across resume flows.", + "1.7": ( + "Persists duplicate-name agent identities across agent-owned state " + "and sandbox resume state." + ), +} +SUPPORTED_SCHEMA_VERSIONS = frozenset(SCHEMA_VERSION_SUMMARIES) + +if CURRENT_SCHEMA_VERSION not in SCHEMA_VERSION_SUMMARIES: + raise AssertionError( + "CURRENT_SCHEMA_VERSION must have a matching entry in SCHEMA_VERSION_SUMMARIES." + ) + +_missing_schema_version_summaries = [ + version for version, summary in SCHEMA_VERSION_SUMMARIES.items() if not summary.strip() +] +if _missing_schema_version_summaries: + raise AssertionError( + "Every supported RunState schema version must have a non-empty summary. " + f"Missing summaries: {', '.join(_missing_schema_version_summaries)}" + ) _FUNCTION_OUTPUT_ADAPTER: TypeAdapter[FunctionCallOutput] = TypeAdapter(FunctionCallOutput) _COMPUTER_OUTPUT_ADAPTER: TypeAdapter[ComputerCallOutput] = TypeAdapter(ComputerCallOutput) @@ -157,6 +187,9 @@ class RunState(Generic[TContext, TAgent]): _current_agent: TAgent | None = None """The agent currently handling the conversation.""" + _starting_agent: TAgent | None = field(default=None, repr=False) + """The root agent used to derive stable duplicate-name identities during resume.""" + _original_input: str | list[Any] = field(default_factory=list) """Original user input prior to any processing.""" @@ -220,6 +253,12 @@ class RunState(Generic[TContext, TAgent]): _agent_tool_state_scope_id: str | None = field(default=None, repr=False) """Private scope id used to isolate agent-tool pending state per RunState instance.""" + _sandbox: dict[str, Any] | None = field(default=None, repr=False) + """Serialized sandbox resume payload for sandbox-aware runs.""" + + _schema_version: str = field(default=CURRENT_SCHEMA_VERSION, repr=False) + """Schema version the snapshot was loaded from for schema-gated resume compatibility.""" + def __init__( self, context: RunContextWrapper[TContext], @@ -234,6 +273,7 @@ def __init__( """Initialize a new RunState.""" self._context = context self._original_input = _clone_original_input(original_input) + self._starting_agent = starting_agent self._current_agent = starting_agent self._max_turns = max_turns self._conversation_id = conversation_id @@ -254,6 +294,8 @@ def __init__( self._current_turn_persisted_item_count = 0 self._tool_use_tracker_snapshot = {} self._trace_state = None + self._sandbox = None + self._schema_version = CURRENT_SCHEMA_VERSION from .agent_tool_state import get_agent_tool_state_scope self._agent_tool_state_scope_id = get_agent_tool_state_scope(context) @@ -498,8 +540,14 @@ def _current_generated_items_merge_marker(self) -> str | None: latest_response_id = ( self._model_responses[-1].response_id if self._model_responses else None ) + agent_identity_keys_by_id = ( + _build_agent_identity_keys_by_id(cast(Agent[Any], self._starting_agent)) + if self._starting_agent is not None + else None + ) serialized_items = [ - self._serialize_item(item) for item in self._last_processed_response.new_items + self._serialize_item(item, agent_identity_keys_by_id=agent_identity_keys_by_id) + for item in self._last_processed_response.new_items ] return json.dumps( { @@ -633,19 +681,33 @@ def to_json( if tool_input is not None: context_entry["tool_input"] = tool_input + agent_identity_keys_by_id = ( + _build_agent_identity_keys_by_id(cast(Agent[Any], self._starting_agent)) + if self._starting_agent is not None + else None + ) + current_agent_entry = _serialize_agent_reference( + cast(Agent[Any], self._current_agent), + agent_identity_keys_by_id=agent_identity_keys_by_id, + ) + result = { "$schemaVersion": CURRENT_SCHEMA_VERSION, "current_turn": self._current_turn, - "current_agent": {"name": self._current_agent.name}, + "current_agent": current_agent_entry, "original_input": original_input_serialized, "model_responses": model_responses, "context": context_entry, "tool_use_tracker": copy.deepcopy(self._tool_use_tracker_snapshot), "max_turns": self._max_turns, "no_active_agent_run": True, - "input_guardrail_results": _serialize_guardrail_results(self._input_guardrail_results), + "input_guardrail_results": _serialize_guardrail_results( + self._input_guardrail_results, + agent_identity_keys_by_id=agent_identity_keys_by_id, + ), "output_guardrail_results": _serialize_guardrail_results( - self._output_guardrail_results + self._output_guardrail_results, + agent_identity_keys_by_id=agent_identity_keys_by_id, ), "tool_input_guardrail_results": _serialize_tool_guardrail_results( self._tool_input_guardrail_results, type_label="tool_input" @@ -660,13 +722,20 @@ def to_json( } generated_items = self._merge_generated_items_with_processed() - result["generated_items"] = [self._serialize_item(item) for item in generated_items] - result["session_items"] = [self._serialize_item(item) for item in list(self._session_items)] + result["generated_items"] = [ + self._serialize_item(item, agent_identity_keys_by_id=agent_identity_keys_by_id) + for item in generated_items + ] + result["session_items"] = [ + self._serialize_item(item, agent_identity_keys_by_id=agent_identity_keys_by_id) + for item in list(self._session_items) + ] result["current_step"] = self._serialize_current_step() result["last_model_response"] = _serialize_last_model_response(model_responses) result["last_processed_response"] = ( self._serialize_processed_response( self._last_processed_response, + agent_identity_keys_by_id=agent_identity_keys_by_id, context_serializer=context_serializer, strict_context=strict_context, include_tracing_api_key=include_tracing_api_key, @@ -678,6 +747,8 @@ def to_json( result["trace"] = self._serialize_trace_data( include_tracing_api_key=include_tracing_api_key ) + if self._sandbox is not None: + result["sandbox"] = copy.deepcopy(self._sandbox) return result @@ -685,6 +756,7 @@ def _serialize_processed_response( self, processed_response: ProcessedResponse, *, + agent_identity_keys_by_id: Mapping[int, str] | None = None, context_serializer: ContextSerializer | None = None, strict_context: bool = False, include_tracing_api_key: bool = False, @@ -710,13 +782,20 @@ def _serialize_processed_response( ) interruptions_data = [ - _serialize_tool_approval_interruption(interruption, include_tool_name=True) + _serialize_tool_approval_interruption( + interruption, + include_tool_name=True, + agent_identity_keys_by_id=agent_identity_keys_by_id, + ) for interruption in processed_response.interruptions if isinstance(interruption, ToolApprovalItem) ] return { - "new_items": [self._serialize_item(item) for item in processed_response.new_items], + "new_items": [ + self._serialize_item(item, agent_identity_keys_by_id=agent_identity_keys_by_id) + for item in processed_response.new_items + ], "tools_used": processed_response.tools_used, **action_groups, "interruptions": interruptions_data, @@ -727,12 +806,20 @@ def _serialize_current_step(self) -> dict[str, Any] | None: # Import at runtime to avoid circular import from .run_internal.run_steps import NextStepInterruption + agent_identity_keys_by_id = ( + _build_agent_identity_keys_by_id(cast(Agent[Any], self._starting_agent)) + if self._starting_agent is not None + else None + ) + if self._current_step is None or not isinstance(self._current_step, NextStepInterruption): return None interruptions_data = [ _serialize_tool_approval_interruption( - item, include_tool_name=item.tool_name is not None + item, + include_tool_name=item.tool_name is not None, + agent_identity_keys_by_id=agent_identity_keys_by_id, ) for item in self._current_step.interruptions if isinstance(item, ToolApprovalItem) @@ -745,14 +832,22 @@ def _serialize_current_step(self) -> dict[str, Any] | None: }, } - def _serialize_item(self, item: RunItem) -> dict[str, Any]: + def _serialize_item( + self, + item: RunItem, + *, + agent_identity_keys_by_id: Mapping[int, str] | None = None, + ) -> dict[str, Any]: """Serialize a run item to JSON-compatible dict.""" raw_item_dict: Any = _serialize_raw_item_value(item.raw_item) result: dict[str, Any] = { "type": item.type, "raw_item": raw_item_dict, - "agent": {"name": item.agent.name}, + "agent": _serialize_agent_reference( + item.agent, + agent_identity_keys_by_id=agent_identity_keys_by_id, + ), } # Add additional fields based on item type @@ -768,9 +863,15 @@ def _serialize_item(self, item: RunItem) -> dict[str, Any]: serialized_output = str(item.output) result["output"] = serialized_output if hasattr(item, "source_agent"): - result["source_agent"] = {"name": item.source_agent.name} + result["source_agent"] = _serialize_agent_reference( + item.source_agent, + agent_identity_keys_by_id=agent_identity_keys_by_id, + ) if hasattr(item, "target_agent"): - result["target_agent"] = {"name": item.target_agent.name} + result["target_agent"] = _serialize_agent_reference( + item.target_agent, + agent_identity_keys_by_id=agent_identity_keys_by_id, + ) if hasattr(item, "tool_name") and item.tool_name is not None: result["tool_name"] = item.tool_name if hasattr(item, "tool_namespace") and item.tool_namespace is not None: @@ -1090,6 +1191,19 @@ def _serialize_raw_item_value(raw_item: Any) -> Any: return raw_item +def _serialize_agent_reference( + agent: Agent[Any], + agent_identity_keys_by_id: Mapping[int, str] | None = None, +) -> dict[str, Any]: + """Serialize an agent reference with an optional duplicate-name identity key.""" + entry: dict[str, Any] = {"name": agent.name} + if agent_identity_keys_by_id is not None: + identity = agent_identity_keys_by_id.get(id(agent)) + if identity is not None and identity != agent.name: + entry["identity"] = identity + return entry + + def _ensure_json_compatible(value: Any) -> Any: try: return json.loads(json.dumps(value, default=str)) @@ -1214,13 +1328,19 @@ def _serialize_mcp_tool(mcp_tool: Any) -> dict[str, Any]: def _serialize_tool_approval_interruption( - interruption: ToolApprovalItem, *, include_tool_name: bool + interruption: ToolApprovalItem, + *, + include_tool_name: bool, + agent_identity_keys_by_id: Mapping[int, str] | None = None, ) -> dict[str, Any]: """Serialize a ToolApprovalItem interruption.""" interruption_dict: dict[str, Any] = { "type": "tool_approval_item", "raw_item": _serialize_raw_item_value(interruption.raw_item), - "agent": {"name": interruption.agent.name}, + "agent": _serialize_agent_reference( + interruption.agent, + agent_identity_keys_by_id=agent_identity_keys_by_id, + ), } if include_tool_name and interruption.tool_name is not None: interruption_dict["tool_name"] = interruption.tool_name @@ -1388,6 +1508,8 @@ def to_state(self) -> RunState[Any, Agent[Any]]: def _serialize_guardrail_results( results: Sequence[InputGuardrailResult | OutputGuardrailResult], + *, + agent_identity_keys_by_id: Mapping[int, str] | None = None, ) -> list[dict[str, Any]]: """Serialize guardrail results for persistence.""" serialized: list[dict[str, Any]] = [] @@ -1404,7 +1526,10 @@ def _serialize_guardrail_results( } if isinstance(result, OutputGuardrailResult): entry["agentOutput"] = result.agent_output - entry["agent"] = {"name": result.agent.name} + entry["agent"] = _serialize_agent_reference( + result.agent, + agent_identity_keys_by_id=agent_identity_keys_by_id, + ) serialized.append(entry) return serialized @@ -1544,6 +1669,7 @@ async def _deserialize_processed_response( context: RunContextWrapper[Any], agent_map: dict[str, Agent[Any]], *, + agent_identity_map: Mapping[str, Agent[Any]] | None = None, scope_id: str | None = None, context_deserializer: ContextDeserializer | None = None, strict_context: bool = False, @@ -1559,7 +1685,11 @@ async def _deserialize_processed_response( Returns: A reconstructed ProcessedResponse instance. """ - new_items = _deserialize_items(processed_response_data.get("new_items", []), agent_map) + new_items = _deserialize_items( + processed_response_data.get("new_items", []), + agent_map, + agent_identity_map=agent_identity_map, + ) if hasattr(current_agent, "get_all_tools"): all_tools = await current_agent.get_all_tools(context) @@ -1811,6 +1941,7 @@ def _resolve_function_tool_name(data: Mapping[str, Any]) -> FunctionToolLookupKe approval_item = _deserialize_tool_approval_item( interruption_data, agent_map=agent_map, + agent_identity_map=agent_identity_map, fallback_agent=current_agent, ) if approval_item is not None: @@ -1855,16 +1986,32 @@ def _deserialize_tool_call_raw_item(normalized_raw_item: Mapping[str, Any]) -> A def _resolve_agent_from_data( agent_data: Any, agent_map: Mapping[str, Agent[Any]], + agent_identity_map: Mapping[str, Agent[Any]] | None = None, fallback_agent: Agent[Any] | None = None, ) -> Agent[Any] | None: """Resolve an agent from serialized data with an optional fallback.""" agent_name = None + agent_identity = None if isinstance(agent_data, Mapping): + agent_identity = agent_data.get("identity") agent_name = agent_data.get("name") elif isinstance(agent_data, str): agent_name = agent_data + if isinstance(agent_identity, str) and agent_identity_map is not None: + resolved = agent_identity_map.get(agent_identity) + if resolved is not None: + return resolved + raise UserError( + "Run state references an agent identity that is not present in the restored graph: " + f"{agent_identity}" + ) + if agent_name: + if agent_identity_map is not None: + resolved = agent_identity_map.get(agent_name) + if resolved is not None: + return resolved return agent_map.get(agent_name) or fallback_agent return fallback_agent @@ -1881,11 +2028,17 @@ def _deserialize_tool_approval_item( item_data: Mapping[str, Any], *, agent_map: Mapping[str, Agent[Any]], + agent_identity_map: Mapping[str, Agent[Any]] | None = None, fallback_agent: Agent[Any] | None = None, pre_normalized_raw_item: Any | None = None, ) -> ToolApprovalItem | None: """Deserialize a ToolApprovalItem from serialized data.""" - agent = _resolve_agent_from_data(item_data.get("agent"), agent_map, fallback_agent) + agent = _resolve_agent_from_data( + item_data.get("agent"), + agent_map, + agent_identity_map, + fallback_agent, + ) if agent is None: return None @@ -2018,6 +2171,7 @@ def _deserialize_output_guardrail_results( results_data: list[dict[str, Any]], *, agent_map: dict[str, Agent[Any]], + agent_identity_map: Mapping[str, Agent[Any]] | None = None, fallback_agent: Agent[Any], ) -> list[OutputGuardrailResult]: """Rehydrate output guardrail results from serialized data.""" @@ -2029,9 +2183,14 @@ def _deserialize_output_guardrail_results( name, guardrail_output, entry_dict = parsed agent_output = entry_dict.get("agentOutput") agent_data = entry_dict.get("agent") - agent_name = agent_data.get("name") if isinstance(agent_data, dict) else None - resolved_agent = agent_map.get(agent_name) if isinstance(agent_name, str) else None - resolved_agent = resolved_agent or fallback_agent + resolved_agent = _resolve_agent_from_data( + agent_data, + agent_map, + agent_identity_map, + fallback_agent, + ) + if resolved_agent is None: + resolved_agent = fallback_agent def _output_guardrail_fn( context: RunContextWrapper[Any], @@ -2134,10 +2293,16 @@ async def _build_run_state_from_json( f"New snapshots are written as version {CURRENT_SCHEMA_VERSION}." ) + agent_identity_map = _build_agent_identity_map(initial_agent) agent_map = _build_agent_map(initial_agent) - current_agent_name = state_json["current_agent"]["name"] - current_agent = agent_map.get(current_agent_name) + current_agent_data = state_json["current_agent"] + current_agent_name = current_agent_data["name"] + current_agent = _resolve_agent_from_data( + current_agent_data, + agent_map, + agent_identity_map=agent_identity_map, + ) if not current_agent: raise UserError(f"Agent {current_agent_name} not found in agent map") @@ -2218,6 +2383,8 @@ async def _build_run_state_from_json( previous_response_id=state_json.get("previous_response_id"), auto_previous_response_id=bool(state_json.get("auto_previous_response_id", False)), ) + state._starting_agent = initial_agent + state._schema_version = schema_version from .agent_tool_state import set_agent_tool_state_scope state._agent_tool_state_scope_id = uuid4().hex @@ -2225,7 +2392,11 @@ async def _build_run_state_from_json( state._current_turn = state_json["current_turn"] state._model_responses = _deserialize_model_responses(state_json.get("model_responses", [])) - state._generated_items = _deserialize_items(state_json.get("generated_items", []), agent_map) + state._generated_items = _deserialize_items( + state_json.get("generated_items", []), + agent_map, + agent_identity_map=agent_identity_map, + ) last_processed_response_data = state_json.get("last_processed_response") if last_processed_response_data and state._context is not None: @@ -2234,6 +2405,7 @@ async def _build_run_state_from_json( current_agent, state._context, agent_map, + agent_identity_map=agent_identity_map, scope_id=state._agent_tool_state_scope_id, context_deserializer=context_deserializer, strict_context=strict_context, @@ -2242,7 +2414,11 @@ async def _build_run_state_from_json( state._last_processed_response = None if "session_items" in state_json: - state._session_items = _deserialize_items(state_json.get("session_items", []), agent_map) + state._session_items = _deserialize_items( + state_json.get("session_items", []), + agent_map, + agent_identity_map=agent_identity_map, + ) else: state._session_items = state._merge_generated_items_with_processed() @@ -2254,6 +2430,7 @@ async def _build_run_state_from_json( state._output_guardrail_results = _deserialize_output_guardrail_results( state_json.get("output_guardrail_results", []), agent_map=agent_map, + agent_identity_map=agent_identity_map, fallback_agent=current_agent, ) state._tool_input_guardrail_results = _deserialize_tool_input_guardrail_results( @@ -2270,7 +2447,11 @@ async def _build_run_state_from_json( "interruptions", current_step_data.get("interruptions", []) ) for item_data in interruptions_data: - approval_item = _deserialize_tool_approval_item(item_data, agent_map=agent_map) + approval_item = _deserialize_tool_approval_item( + item_data, + agent_map=agent_map, + agent_identity_map=agent_identity_map, + ) if approval_item is not None: interruptions.append(approval_item) @@ -2294,29 +2475,25 @@ async def _build_run_state_from_json( state._trace_state = TraceState.from_json(trace_data) else: state._trace_state = None + sandbox_data = state_json.get("sandbox") + state._sandbox = dict(sandbox_data) if isinstance(sandbox_data, Mapping) else None return state -def _build_agent_map(initial_agent: Agent[Any]) -> dict[str, Agent[Any]]: - """Build a map of agent names to agents by traversing handoffs. - - Args: - initial_agent: The starting agent. - - Returns: - Dictionary mapping agent names to agent instances. - """ - agent_map: dict[str, Agent[Any]] = {} +def _iter_agent_graph(initial_agent: Agent[Any]) -> Iterator[Agent[Any]]: + """Yield agents reachable from the starting agent in breadth-first order.""" queue: deque[Agent[Any]] = deque([initial_agent]) + seen_agent_ids: set[int] = set() while queue: current = queue.popleft() - if current.name in agent_map: + current_id = id(current) + if current_id in seen_agent_ids: continue - agent_map[current.name] = current + seen_agent_ids.add(current_id) + yield current - # Add handoff agents to the queue for handoff_item in current.handoffs: handoff_agent: Any | None = None handoff_agent_name: str | None = None @@ -2329,8 +2506,6 @@ def _build_agent_map(initial_agent: Agent[Any]) -> dict[str, Agent[Any]]: ) if isinstance(candidate_name, str): handoff_agent_name = candidate_name - if handoff_agent_name in agent_map: - continue handoff_ref = getattr(handoff_item, "_agent_ref", None) handoff_agent = handoff_ref() if callable(handoff_ref) else None @@ -2368,12 +2543,8 @@ def _build_agent_map(initial_agent: Agent[Any]) -> dict[str, Agent[Any]]: candidate_name = getattr(handoff_agent, "name", None) handoff_agent_name = candidate_name if isinstance(candidate_name, str) else None - if ( - handoff_agent is not None - and handoff_agent_name - and handoff_agent_name not in agent_map - ): - queue.append(cast(Any, handoff_agent)) + if handoff_agent is not None and handoff_agent_name: + queue.append(cast(Agent[Any], handoff_agent)) # Include agent-as-tool instances so nested approvals can be restored. tools = getattr(current, "tools", None) @@ -2383,9 +2554,391 @@ def _build_agent_map(initial_agent: Agent[Any]) -> dict[str, Agent[Any]]: continue tool_agent = getattr(tool, "_agent_instance", None) tool_agent_name = getattr(tool_agent, "name", None) - if tool_agent and tool_agent_name and tool_agent_name not in agent_map: + if tool_agent and tool_agent_name: queue.append(tool_agent) + +def _allocate_unique_agent_identity(agent_name: str, used_identities: set[str]) -> str: + """Return a deterministic identity key without colliding with literal agent names.""" + candidate = agent_name + next_index = 1 + while candidate in used_identities: + next_index += 1 + candidate = f"{agent_name}#{next_index}" + used_identities.add(candidate) + return candidate + + +def _identity_type_name(value: Any) -> str: + return f"{type(value).__module__}.{type(value).__qualname__}" + + +def _callable_identity_name(value: Any) -> str: + module = getattr(value, "__module__", type(value).__module__) + qualname = getattr(value, "__qualname__", type(value).__qualname__) + return f"{module}.{qualname}" + + +def _normalize_identity_value(value: Any) -> Any: + if value is None or isinstance(value, (str, int, float, bool)): + return value + if isinstance(value, (bytes, bytearray)): + return {"type": "bytes", "length": len(value)} + if callable(value): + return {"callable": _callable_identity_name(value)} + if dataclasses.is_dataclass(value): + return { + "dataclass": _identity_type_name(value), + "value": _normalize_identity_value(dataclasses.asdict(cast(Any, value))), + } + if hasattr(value, "model_dump"): + try: + dumped = value.model_dump(exclude_unset=True) + except TypeError: + dumped = value.model_dump() + return { + "model": _identity_type_name(value), + "value": _normalize_identity_value(dumped), + } + if isinstance(value, Mapping): + return { + str(key): _normalize_identity_value(item) + for key, item in sorted(value.items(), key=lambda pair: str(pair[0])) + } + if isinstance(value, Sequence) and not isinstance(value, (str, bytes, bytearray)): + return [_normalize_identity_value(item) for item in value] + + value_name = getattr(value, "name", None) + if isinstance(value_name, str): + return {"type": _identity_type_name(value), "name": value_name} + return {"type": _identity_type_name(value)} + + +def _stable_identity_text(value: Any) -> str: + return json.dumps( + _normalize_identity_value(value), + sort_keys=True, + separators=(",", ":"), + ) + + +def _tool_identity_signature(tool: Any) -> dict[str, Any]: + signature: dict[str, Any] = { + "type": _identity_type_name(tool), + "name": getattr(tool, "name", None), + } + namespace = get_function_tool_namespace(tool) + if namespace is not None: + signature["namespace"] = namespace + qualified_name = get_function_tool_qualified_name(tool) + if qualified_name is not None: + signature["qualified_name"] = qualified_name + if hasattr(tool, "environment"): + signature["environment"] = _normalize_identity_value(tool.environment) + if getattr(tool, "_is_agent_tool", False): + nested_agent = getattr(tool, "_agent_instance", None) + signature["agent_tool_target"] = getattr(nested_agent, "name", None) + return signature + + +_THREADING_LOCK_TYPES = (type(threading.Lock()), type(threading.RLock())) + + +def _is_capability_runtime_only_value(value: Any) -> bool: + return isinstance( + value, + ( + BaseSandboxSession, + asyncio.Event, + asyncio.Lock, + asyncio.Semaphore, + asyncio.Condition, + threading.Event, + *_THREADING_LOCK_TYPES, + ), + ) + + +def _normalize_capability_identity_value( + value: Any, + *, + seen: set[int] | None = None, +) -> Any: + if seen is None: + seen = set() + + if value is None or isinstance(value, (str, int, float, bool)): + return value + if isinstance(value, Path): + return value.as_posix() + if isinstance(value, (bytes, bytearray)): + return {"type": "bytes", "length": len(value)} + if callable(value): + return {"callable": _callable_identity_name(value)} + if _is_capability_runtime_only_value(value): + return {"runtime_only": _identity_type_name(value)} + if isinstance( + value, + ( + ApplyPatchTool, + ComputerTool, + FunctionTool, + HostedMCPTool, + LocalShellTool, + ShellTool, + ), + ): + return _tool_identity_signature(value) + + object_id = id(value) + if object_id in seen: + return {"recursive": _identity_type_name(value)} + + if dataclasses.is_dataclass(value): + seen.add(object_id) + try: + merged_fields = { + field.name: getattr(value, field.name) for field in dataclasses.fields(value) + } + if hasattr(value, "__dict__"): + for name, item in vars(value).items(): + if name.startswith("_") or name in merged_fields: + continue + merged_fields[name] = item + return { + "dataclass": _identity_type_name(value), + "value": { + name: _normalize_capability_identity_value( + item, + seen=seen, + ) + for name, item in sorted(merged_fields.items()) + }, + } + finally: + seen.remove(object_id) + + if hasattr(value, "model_dump"): + seen.add(object_id) + try: + try: + dumped = value.model_dump(mode="json", round_trip=True) + except TypeError: + dumped = value.model_dump(mode="json") + return { + "model": _identity_type_name(value), + "value": _normalize_capability_identity_value(dumped, seen=seen), + } + finally: + seen.remove(object_id) + + if isinstance(value, Mapping): + seen.add(object_id) + try: + return { + str(key): _normalize_capability_identity_value(item, seen=seen) + for key, item in sorted(value.items(), key=lambda pair: str(pair[0])) + } + finally: + seen.remove(object_id) + + if isinstance(value, (set, frozenset)): + seen.add(object_id) + try: + normalized_items = [ + _normalize_capability_identity_value(item, seen=seen) for item in value + ] + return sorted(normalized_items, key=_stable_identity_text) + finally: + seen.remove(object_id) + + if isinstance(value, Sequence) and not isinstance(value, (str, bytes, bytearray)): + seen.add(object_id) + try: + return [_normalize_capability_identity_value(item, seen=seen) for item in value] + finally: + seen.remove(object_id) + + if hasattr(value, "__dict__"): + seen.add(object_id) + try: + return { + "object": _identity_type_name(value), + "value": { + name: _normalize_capability_identity_value(item, seen=seen) + for name, item in sorted(vars(value).items()) + if not name.startswith("_") + }, + } + finally: + seen.remove(object_id) + + value_name = getattr(value, "name", None) + if isinstance(value_name, str): + return {"type": _identity_type_name(value), "name": value_name} + return {"type": _identity_type_name(value)} + + +def _capability_identity_signature(capability: Any) -> dict[str, Any]: + return { + "type": _identity_type_name(capability), + "value": _normalize_capability_identity_value(capability), + } + + +def _handoff_identity_signature(handoff_item: Agent[Any] | Handoff[Any, Any]) -> dict[str, Any]: + if isinstance(handoff_item, Handoff): + tool_name = getattr(handoff_item, "tool_name", None) + if not isinstance(tool_name, str): + tool_name = getattr(handoff_item, "name", None) + agent_name = getattr(handoff_item, "agent_name", None) + return { + "type": _identity_type_name(handoff_item), + "tool_name": tool_name, + "agent_name": agent_name if isinstance(agent_name, str) else None, + "input_filter": _normalize_identity_value(getattr(handoff_item, "input_filter", None)), + "nest_handoff_history": getattr(handoff_item, "nest_handoff_history", None), + } + + return { + "type": _identity_type_name(handoff_item), + "agent_name": getattr(handoff_item, "name", None), + } + + +def _agent_identity_signature(agent: Agent[Any]) -> str: + signature: dict[str, Any] = { + "agent_type": _identity_type_name(agent), + "handoff_description": getattr(agent, "handoff_description", None), + "instructions": _normalize_identity_value(getattr(agent, "instructions", None)), + "prompt": _normalize_identity_value(getattr(agent, "prompt", None)), + "model": _normalize_identity_value(getattr(agent, "model", None)), + "model_settings": _normalize_identity_value(getattr(agent, "model_settings", None)), + "mcp_config": _normalize_capability_identity_value(getattr(agent, "mcp_config", None)), + "hooks": _normalize_capability_identity_value(getattr(agent, "hooks", None)), + "input_guardrails": sorted( + _stable_identity_text(_normalize_capability_identity_value(guardrail)) + for guardrail in getattr(agent, "input_guardrails", []) + ), + "output_guardrails": sorted( + _stable_identity_text(_normalize_capability_identity_value(guardrail)) + for guardrail in getattr(agent, "output_guardrails", []) + ), + "output_type": _normalize_identity_value(getattr(agent, "output_type", None)), + "tool_use_behavior": _normalize_capability_identity_value( + getattr(agent, "tool_use_behavior", None) + ), + "reset_tool_choice": getattr(agent, "reset_tool_choice", None), + "tools": sorted( + _stable_identity_text(_tool_identity_signature(tool)) + for tool in getattr(agent, "tools", []) + ), + "handoffs": sorted( + _stable_identity_text(_handoff_identity_signature(handoff_item)) + for handoff_item in getattr(agent, "handoffs", []) + ), + "mcp_servers": sorted( + _stable_identity_text(server) for server in getattr(agent, "mcp_servers", []) + ), + } + + default_manifest = getattr(agent, "default_manifest", None) + if default_manifest is not None: + signature["default_manifest"] = _normalize_capability_identity_value(default_manifest) + + developer_instructions = getattr(agent, "developer_instructions", None) + if developer_instructions is not None: + signature["developer_instructions"] = developer_instructions + + capabilities = getattr(agent, "capabilities", None) + if isinstance(capabilities, Sequence): + signature["capabilities"] = sorted( + _stable_identity_text(_capability_identity_signature(capability)) + for capability in capabilities + ) + + return _stable_identity_text(signature) + + +def _agent_identity_sort_key( + agent: Agent[Any], + *, + root_agent: Agent[Any], + original_index: int, +) -> tuple[int, str, int]: + return ( + 0 if agent is root_agent else 1, + _agent_identity_signature(agent), + original_index, + ) + + +def _build_agent_identity_map(initial_agent: Agent[Any]) -> dict[str, Agent[Any]]: + """Build a stable identity map that preserves duplicate agent names.""" + ordered_agents = list(_iter_agent_graph(initial_agent)) + original_indices = {id(agent): index for index, agent in enumerate(ordered_agents)} + literal_names = {agent.name for agent in ordered_agents} + agents_by_name: dict[str, list[Agent[Any]]] = {} + for agent in ordered_agents: + agents_by_name.setdefault(agent.name, []).append(agent) + + agent_identity_map: dict[str, Agent[Any]] = {} + used_identities: set[str] = set() + processed_names: set[str] = set() + + for agent in ordered_agents: + agent_name = agent.name + if agent_name in processed_names: + continue + processed_names.add(agent_name) + + group = agents_by_name[agent_name] + sorted_group = sorted( + group, + key=lambda candidate: _agent_identity_sort_key( + candidate, + root_agent=initial_agent, + original_index=original_indices[id(candidate)], + ), + ) + + base_agent = sorted_group[0] + used_identities.add(agent_name) + agent_identity_map[agent_name] = base_agent + + next_index = 2 + for duplicate_agent in sorted_group[1:]: + candidate = f"{agent_name}#{next_index}" + while candidate in used_identities or candidate in literal_names: + next_index += 1 + candidate = f"{agent_name}#{next_index}" + used_identities.add(candidate) + agent_identity_map[candidate] = duplicate_agent + next_index += 1 + + return agent_identity_map + + +def _build_agent_identity_keys_by_id(initial_agent: Agent[Any]) -> dict[int, str]: + """Build stable identity keys for the reachable agent graph.""" + return { + id(agent): identity for identity, agent in _build_agent_identity_map(initial_agent).items() + } + + +def _build_agent_map(initial_agent: Agent[Any]) -> dict[str, Agent[Any]]: + """Build a map of agent names to agents by traversing handoffs. + + Args: + initial_agent: The starting agent. + + Returns: + Dictionary mapping agent names to agent instances. + """ + agent_map: dict[str, Agent[Any]] = {} + for agent in _iter_agent_graph(initial_agent): + agent_map.setdefault(agent.name, agent) + return agent_map @@ -2426,7 +2979,10 @@ def _deserialize_model_responses(responses_data: list[dict[str, Any]]) -> list[M def _deserialize_items( - items_data: list[dict[str, Any]], agent_map: dict[str, Agent[Any]] + items_data: list[dict[str, Any]], + agent_map: dict[str, Agent[Any]], + *, + agent_identity_map: Mapping[str, Agent[Any]] | None = None, ) -> list[RunItem]: """Deserialize run items from JSON data. @@ -2456,7 +3012,11 @@ def _resolve_agent_info( elif isinstance(raw_agent, str): candidate_name = raw_agent - agent_candidate = _resolve_agent_from_data(raw_agent, agent_map) + agent_candidate = _resolve_agent_from_data( + raw_agent, + agent_map, + agent_identity_map, + ) if agent_candidate: return agent_candidate, agent_candidate.name @@ -2537,8 +3097,16 @@ def _resolve_agent_info( result.append(HandoffCallItem(agent=agent, raw_item=raw_item_handoff)) elif item_type == "handoff_output_item": - source_agent = _resolve_agent_from_data(item_data.get("source_agent"), agent_map) - target_agent = _resolve_agent_from_data(item_data.get("target_agent"), agent_map) + source_agent = _resolve_agent_from_data( + item_data.get("source_agent"), + agent_map, + agent_identity_map, + ) + target_agent = _resolve_agent_from_data( + item_data.get("target_agent"), + agent_map, + agent_identity_map, + ) # If we cannot resolve both agents, skip this item gracefully if not source_agent or not target_agent: @@ -2601,12 +3169,15 @@ def _resolve_agent_info( approval_item = _deserialize_tool_approval_item( item_data, agent_map=agent_map, + agent_identity_map=agent_identity_map, fallback_agent=agent, pre_normalized_raw_item=normalized_raw_item, ) if approval_item is not None: result.append(approval_item) + except UserError: + raise except Exception as e: logger.warning(f"Failed to deserialize item of type {item_type}: {e}") continue diff --git a/src/agents/sandbox/__init__.py b/src/agents/sandbox/__init__.py new file mode 100644 index 0000000000..38a6dd8abb --- /dev/null +++ b/src/agents/sandbox/__init__.py @@ -0,0 +1,46 @@ +from __future__ import annotations + +from ..run_config import SandboxRunConfig +from .capabilities import Capability +from .entries import Dir, LocalFile +from .errors import ( + ErrorCode, + ExecTimeoutError, + ExecTransportError, + UniversalComputerError, + WorkspaceArchiveReadError, + WorkspaceArchiveWriteError, + WorkspaceReadNotFoundError, + WorkspaceWriteTypeError, +) +from .manifest import Manifest +from .sandbox_agent import SandboxAgent +from .snapshot import ( + LocalSnapshot, + LocalSnapshotSpec, + SnapshotSpec, + resolve_snapshot, +) +from .types import ExecResult + +__all__ = [ + "Capability", + "Dir", + "ErrorCode", + "ExecResult", + "ExecTimeoutError", + "ExecTransportError", + "LocalFile", + "LocalSnapshot", + "LocalSnapshotSpec", + "Manifest", + "SandboxAgent", + "SandboxRunConfig", + "SnapshotSpec", + "UniversalComputerError", + "WorkspaceArchiveReadError", + "WorkspaceArchiveWriteError", + "WorkspaceReadNotFoundError", + "WorkspaceWriteTypeError", + "resolve_snapshot", +] diff --git a/src/agents/sandbox/capabilities/__init__.py b/src/agents/sandbox/capabilities/__init__.py new file mode 100644 index 0000000000..62da00db26 --- /dev/null +++ b/src/agents/sandbox/capabilities/__init__.py @@ -0,0 +1,3 @@ +from .capability import Capability + +__all__ = ["Capability"] diff --git a/src/agents/sandbox/capabilities/capability.py b/src/agents/sandbox/capabilities/capability.py new file mode 100644 index 0000000000..a6d85e4eb6 --- /dev/null +++ b/src/agents/sandbox/capabilities/capability.py @@ -0,0 +1,80 @@ +import asyncio +import copy +import threading +from dataclasses import dataclass +from typing import Any + +from ...tool import Tool +from ..manifest import Manifest +from ..session.base_sandbox_session import BaseSandboxSession + + +@dataclass +class Capability: + type: str + + def clone(self) -> "Capability": + """Return a per-run copy of this capability.""" + cloned = copy.copy(self) + if hasattr(self, "__dict__"): + for name, value in self.__dict__.items(): + setattr(cloned, name, _clone_capability_value(value)) + return cloned + + def bind(self, session: BaseSandboxSession) -> None: + """Bind a live session to this plugin (default no-op).""" + _ = session + return + + def tools(self) -> list[Tool]: + return [] + + def process_manifest(self, manifest: Manifest) -> Manifest: + return manifest + + async def instructions(self, manifest: Manifest) -> str | None: + """Return a deterministic instruction fragment for this plugin.""" + _ = manifest + return None + + +def _clone_capability_value(value: Any) -> Any: + if getattr(type(value), "__module__", "").startswith("agents.tool"): + return value + if isinstance( + value, + ( + BaseSandboxSession, + asyncio.Event, + asyncio.Lock, + asyncio.Semaphore, + asyncio.Condition, + threading.Event, + type(threading.Lock()), + type(threading.RLock()), + ), + ): + return value + if isinstance(value, list): + return [_clone_capability_value(item) for item in value] + if isinstance(value, dict): + return { + _clone_capability_value(key): _clone_capability_value(item) + for key, item in value.items() + } + if isinstance(value, set): + return {_clone_capability_value(item) for item in value} + if isinstance(value, tuple): + return tuple(_clone_capability_value(item) for item in value) + if isinstance(value, bytearray): + return bytearray(value) + if hasattr(value, "__dict__"): + cloned = copy.copy(value) + for name, nested in value.__dict__.items(): + setattr(cloned, name, _clone_capability_value(nested)) + return cloned + try: + return copy.deepcopy(value) + except Exception: + return value + return value diff --git a/src/agents/sandbox/entries/__init__.py b/src/agents/sandbox/entries/__init__.py new file mode 100644 index 0000000000..6c77820752 --- /dev/null +++ b/src/agents/sandbox/entries/__init__.py @@ -0,0 +1,34 @@ +from __future__ import annotations + +from .artifacts import Dir, File, GitRepo, LocalDir, LocalFile +from .base import BaseEntry, resolve_workspace_path +from .mounts import ( + AzureBlobMount, + FuseMountPattern, + GCSMount, + Mount, + MountPattern, + MountPatternBase, + MountpointMountPattern, + RcloneMountPattern, + S3Mount, +) + +__all__ = [ + "AzureBlobMount", + "BaseEntry", + "Dir", + "File", + "FuseMountPattern", + "GCSMount", + "GitRepo", + "LocalDir", + "LocalFile", + "Mount", + "MountPattern", + "MountPatternBase", + "MountpointMountPattern", + "RcloneMountPattern", + "S3Mount", + "resolve_workspace_path", +] diff --git a/src/agents/sandbox/entries/artifacts.py b/src/agents/sandbox/entries/artifacts.py new file mode 100644 index 0000000000..a9109bb0a0 --- /dev/null +++ b/src/agents/sandbox/entries/artifacts.py @@ -0,0 +1,326 @@ +from __future__ import annotations + +import io +import re +import uuid +from collections.abc import Awaitable, Callable, Mapping +from pathlib import Path +from typing import TYPE_CHECKING, Literal + +from pydantic import Field, field_serializer, field_validator + +from ..errors import ( + GitCloneError, + GitCopyError, + GitMissingInImageError, + LocalChecksumError, + LocalDirReadError, + LocalFileReadError, +) +from ..materialization import MaterializedFile, gather_in_order +from ..types import ExecResult +from ..util.checksums import sha256_file +from .base import BaseEntry + +if TYPE_CHECKING: + from ..session.base_sandbox_session import BaseSandboxSession + +_COMMIT_REF_RE = re.compile(r"[0-9a-fA-F]{7,40}") + + +class Dir(BaseEntry): + type: Literal["dir"] = "dir" + is_dir: bool = True + children: dict[str | Path, BaseEntry] = Field(default_factory=dict) + + @field_validator("children", mode="before") + @classmethod + def _parse_children(cls, value: object) -> dict[str | Path, BaseEntry]: + if value is None: + return {} + if not isinstance(value, Mapping): + raise TypeError(f"Artifact mapping must be a mapping, got {type(value).__name__}") + return {key: BaseEntry.parse(entry) for key, entry in value.items()} + + @field_serializer("children", when_used="json") + def _serialize_children(self, children: Mapping[str | Path, BaseEntry]) -> dict[str, object]: + out: dict[str, object] = {} + for key, entry in children.items(): + key_str = key.as_posix() if isinstance(key, Path) else str(key) + out[key_str] = entry.model_dump(mode="json") + return out + + def model_post_init(self, context: object, /) -> None: + _ = context + self.permissions.directory = True + + async def apply( + self, + session: BaseSandboxSession, + dest: Path, + base_dir: Path, + ) -> list[MaterializedFile]: + await session.mkdir(dest, parents=True) + await self._apply_metadata(session, dest) + return await session._apply_entry_batch( + [(dest / Path(rel_dest), artifact) for rel_dest, artifact in self.children.items()], + base_dir=base_dir, + ) + + +class File(BaseEntry): + type: Literal["file"] = "file" + content: bytes + + async def apply( + self, + session: BaseSandboxSession, + dest: Path, + base_dir: Path, + ) -> list[MaterializedFile]: + await session.write(dest, io.BytesIO(self.content)) + await self._apply_metadata(session, dest) + return [] + + +class LocalFile(BaseEntry): + type: Literal["local_file"] = "local_file" + src: Path + + async def apply( + self, + session: BaseSandboxSession, + dest: Path, + base_dir: Path, + ) -> list[MaterializedFile]: + src = (base_dir / self.src).resolve() + try: + checksum = sha256_file(src) + except OSError as e: + raise LocalChecksumError(src=src, cause=e) from e + await session.mkdir(Path(dest).parent, parents=True) + try: + with src.open("rb") as f: + await session.write(dest, f) + except OSError as e: + raise LocalFileReadError(src=src, cause=e) from e + await self._apply_metadata(session, dest) + return [MaterializedFile(path=dest, sha256=checksum)] + + +class LocalDir(BaseEntry): + type: Literal["local_dir"] = "local_dir" + is_dir: bool = True + src: Path | None = Field(default=None) + + def model_post_init(self, context: object, /) -> None: + _ = context + self.permissions.directory = True + + async def apply( + self, + session: BaseSandboxSession, + dest: Path, + base_dir: Path, + ) -> list[MaterializedFile]: + files: list[MaterializedFile] = [] + if self.src: + src_root = (base_dir / self.src).resolve() + if not src_root.exists(): + raise LocalDirReadError(src=src_root, context={"reason": "path_not_found"}) + # Minimal v1: copy all files recursively. + try: + await session.mkdir(dest, parents=True) + files = [] + local_files = [child for child in src_root.rglob("*") if child.is_file()] + + def _make_copy_task(child: Path) -> Callable[[], Awaitable[MaterializedFile]]: + async def _copy() -> MaterializedFile: + return await self._copy_local_dir_file( + session=session, + src_root=src_root, + src=child, + dest_root=dest, + ) + + return _copy + + copied_files = await gather_in_order( + [_make_copy_task(child) for child in local_files] + ) + files.extend(copied_files) + except OSError as e: + raise LocalDirReadError(src=src_root, cause=e) from e + await self._apply_metadata(session, dest) + else: + await session.mkdir(dest, parents=True) + await self._apply_metadata(session, dest) + return files + + async def _copy_local_dir_file( + self, + *, + session: BaseSandboxSession, + src_root: Path, + src: Path, + dest_root: Path, + ) -> MaterializedFile: + rel_child = src.relative_to(src_root) + child_dest = dest_root / rel_child + try: + checksum = sha256_file(src) + except OSError as e: + raise LocalChecksumError(src=src, cause=e) from e + await session.mkdir(child_dest.parent, parents=True) + try: + with src.open("rb") as f: + await session.write(child_dest, f) + except OSError as e: + raise LocalFileReadError(src=src, cause=e) from e + return MaterializedFile(path=child_dest, sha256=checksum) + + +class GitRepo(BaseEntry): + type: Literal["git_repo"] = "git_repo" + is_dir: bool = True + host: str = "github.com" + repo: str # "owner/name" (or any host-specific path) + ref: str # tag/branch/sha + subpath: str | None = None + + def model_post_init(self, context: object, /) -> None: + _ = context + self.permissions.directory = True + + async def apply( + self, + session: BaseSandboxSession, + dest: Path, + base_dir: Path, + ) -> list[MaterializedFile]: + # Ensure git exists in the container. + git_check = await session.exec("command -v git >/dev/null 2>&1") + if not git_check.ok(): + context: dict[str, object] = {"repo": self.repo, "ref": self.ref} + image = getattr(session.state, "image", None) + if image is not None: + context["image"] = image + raise GitMissingInImageError(context=context) + + tmp_dir = f"/tmp/uc-git-{session.state.session_id.hex}-{uuid.uuid4().hex}" + url = f"https://{self.host}/{self.repo}.git" + + _ = await session.exec("rm", "-rf", "--", tmp_dir, shell=False) + clone_error: ExecResult | None = None + if self._looks_like_commit_ref(self.ref): + clone = await self._fetch_commit_ref(session=session, url=url, tmp_dir=tmp_dir) + if not clone.ok(): + clone_error = clone + _ = await session.exec("rm", "-rf", "--", tmp_dir, shell=False) + clone = await self._clone_named_ref(session=session, url=url, tmp_dir=tmp_dir) + else: + clone = await self._clone_named_ref(session=session, url=url, tmp_dir=tmp_dir) + if not clone.ok(): + if clone_error is not None: + clone = clone_error + raise GitCloneError( + url=url, + ref=self.ref, + stderr=clone.stderr.decode("utf-8", errors="replace"), + context={"repo": self.repo, "subpath": self.subpath}, + ) + + git_src_root: str = tmp_dir + if self.subpath is not None: + git_src_root = f"{tmp_dir}/{self.subpath.lstrip('/')}" + + # Copy into destination in the container. + await session.mkdir(dest, parents=True) + copy = await session.exec("cp", "-R", "--", f"{git_src_root}/.", f"{dest}/", shell=False) + if not copy.ok(): + raise GitCopyError( + src_root=git_src_root, + dest=dest, + stderr=copy.stderr.decode("utf-8", errors="replace"), + context={"repo": self.repo, "ref": self.ref, "subpath": self.subpath}, + ) + + _ = await session.exec("rm", "-rf", "--", tmp_dir, shell=False) + await self._apply_metadata(session, dest) + + # Receipt: leave checksums empty for now. (Computing them would + # require reading each file back out of the container.) + return [] + + @staticmethod + def _looks_like_commit_ref(ref: str) -> bool: + return _COMMIT_REF_RE.fullmatch(ref) is not None + + async def _clone_named_ref( + self, + *, + session: BaseSandboxSession, + url: str, + tmp_dir: str, + ) -> ExecResult: + return await session.exec( + "git", + "clone", + "--depth", + "1", + "--no-tags", + "--branch", + self.ref, + url, + tmp_dir, + shell=False, + ) + + async def _fetch_commit_ref( + self, + *, + session: BaseSandboxSession, + url: str, + tmp_dir: str, + ) -> ExecResult: + init = await session.exec("git", "init", tmp_dir, shell=False) + if not init.ok(): + return init + + remote_add = await session.exec( + "git", + "-C", + tmp_dir, + "remote", + "add", + "origin", + url, + shell=False, + ) + if not remote_add.ok(): + return remote_add + + fetch = await session.exec( + "git", + "-C", + tmp_dir, + "fetch", + "--depth", + "1", + "--no-tags", + "origin", + self.ref, + shell=False, + ) + if not fetch.ok(): + return fetch + + return await session.exec( + "git", + "-C", + tmp_dir, + "checkout", + "--detach", + "FETCH_HEAD", + shell=False, + ) diff --git a/src/agents/sandbox/entries/base.py b/src/agents/sandbox/entries/base.py new file mode 100644 index 0000000000..0f4a9c41df --- /dev/null +++ b/src/agents/sandbox/entries/base.py @@ -0,0 +1,147 @@ +from __future__ import annotations + +import abc +import builtins +import inspect +import stat +from collections.abc import Mapping +from pathlib import Path +from typing import TYPE_CHECKING, ClassVar + +from pydantic import BaseModel, Field + +from ..errors import InvalidManifestPathError +from ..materialization import MaterializedFile +from ..types import FileMode, Group, Permissions, User + +if TYPE_CHECKING: + from ..session.base_sandbox_session import BaseSandboxSession + + +def resolve_workspace_path( + workspace_root: Path, + rel: str | Path, + *, + allow_absolute_within_root: bool = False, +) -> Path: + rel = Path(rel) + workspace_root = Path(workspace_root) + + if rel.is_absolute(): + if not allow_absolute_within_root: + raise InvalidManifestPathError(rel=rel, reason="absolute") + resolved_workspace_root = workspace_root.resolve(strict=False) + resolved_rel = rel.resolve(strict=False) + try: + resolved_rel.relative_to(resolved_workspace_root) + except ValueError as exc: + raise InvalidManifestPathError(rel=rel, reason="absolute", cause=exc) from exc + return resolved_rel + + if ".." in rel.parts: + raise InvalidManifestPathError(rel=rel, reason="escape_root") + + resolved = workspace_root / rel if rel.parts else workspace_root + if allow_absolute_within_root and resolved.is_absolute(): + try: + resolved.relative_to(workspace_root) + except ValueError as exc: + raise InvalidManifestPathError(rel=rel, reason="escape_root", cause=exc) from exc + return resolved + + +class BaseEntry(BaseModel, abc.ABC): + type: str + _subclass_registry: ClassVar[dict[str, builtins.type[BaseEntry]]] = {} + + description: str | None = Field(default=None) + ephemeral: bool = Field(default=False) + group: Group | User | None = Field(default=None) + # Whether this entry should be treated as a directory in the sandbox filesystem. + # Concrete subclasses override this (e.g. Dir/Mount types -> True). + is_dir: bool = Field(default=False) + permissions: Permissions = Field( + default_factory=lambda: Permissions( + owner=FileMode.ALL, + group=FileMode.READ | FileMode.EXEC, + other=FileMode.READ | FileMode.EXEC, + ) + ) + + @classmethod + def __pydantic_init_subclass__(cls, **kwargs: object) -> None: + super().__pydantic_init_subclass__(**kwargs) + + type_field = cls.model_fields.get("type") + type_default = type_field.default if type_field is not None else None + if not isinstance(type_default, str) or type_default == "": + if inspect.isabstract(cls): + return + raise TypeError(f"{cls.__name__} must define a non-empty string default for `type`") + + cls._register_subclass(cls, allow_override=False) + + @classmethod + def _register_subclass( + cls, + entry_cls: builtins.type[BaseEntry], + *, + allow_override: bool = False, + ) -> builtins.type[BaseEntry]: + type_field = entry_cls.model_fields.get("type") + type_default = type_field.default if type_field is not None else None + if not isinstance(type_default, str) or type_default == "": + raise ValueError(f"{entry_cls.__name__} must define a string `type` field default") + + existing = BaseEntry._subclass_registry.get(type_default) + if existing is not None and existing is not entry_cls and not allow_override: + raise ValueError( + f"Artifact type `{type_default}` is already registered to {existing.__name__}; " + f"refusing to register {entry_cls.__name__}" + ) + + BaseEntry._subclass_registry[type_default] = entry_cls + return entry_cls + + @classmethod + def registered_types(cls) -> dict[str, builtins.type[BaseEntry]]: + return dict(BaseEntry._subclass_registry) + + @classmethod + def parse(cls, payload: object) -> BaseEntry: + if isinstance(payload, BaseEntry): + return payload + if not isinstance(payload, Mapping): + raise TypeError( + f"Artifact entry must be a BaseEntry or mapping, got {type(payload).__name__}" + ) + + entry_type = payload.get("type") + if not isinstance(entry_type, str): + raise ValueError("Artifact entry mapping must include a string `type` field") + + entry_cls = BaseEntry._subclass_registry.get(entry_type) + if entry_cls is None: + known = ", ".join(sorted(BaseEntry._subclass_registry)) or "" + raise ValueError(f"Unknown artifact type `{entry_type}`. Registered types: {known}") + return entry_cls.model_validate(dict(payload)) + + async def _apply_metadata( + self, + session: BaseSandboxSession, + dest: Path, + ) -> None: + if self.group is not None: + await session.exec("chgrp", self.group.name, str(dest), shell=False) + + chmod_perms = f"{stat.S_IMODE(self.permissions.to_mode()):o}".zfill(4) + await session.exec("chmod", chmod_perms, str(dest), shell=False) + + @abc.abstractmethod + async def apply( + self, + session: BaseSandboxSession, + dest: Path, + base_dir: Path, + ) -> list[MaterializedFile]: + raise NotImplementedError diff --git a/src/agents/sandbox/entries/mounts/__init__.py b/src/agents/sandbox/entries/mounts/__init__.py new file mode 100644 index 0000000000..5e68010de6 --- /dev/null +++ b/src/agents/sandbox/entries/mounts/__init__.py @@ -0,0 +1,23 @@ +from __future__ import annotations + +from .base import Mount +from .patterns import ( + FuseMountPattern, + MountPattern, + MountPatternBase, + MountpointMountPattern, + RcloneMountPattern, +) +from .providers import AzureBlobMount, GCSMount, S3Mount + +__all__ = [ + "AzureBlobMount", + "FuseMountPattern", + "GCSMount", + "Mount", + "MountPattern", + "MountPatternBase", + "MountpointMountPattern", + "RcloneMountPattern", + "S3Mount", +] diff --git a/src/agents/sandbox/entries/mounts/base.py b/src/agents/sandbox/entries/mounts/base.py new file mode 100644 index 0000000000..a18b461f52 --- /dev/null +++ b/src/agents/sandbox/entries/mounts/base.py @@ -0,0 +1,107 @@ +from __future__ import annotations + +import abc +import warnings +from pathlib import Path +from typing import TYPE_CHECKING + +from pydantic import Field + +from ...materialization import MaterializedFile +from ...types import FileMode, Permissions +from ..base import BaseEntry + +if TYPE_CHECKING: + from ...session.base_sandbox_session import BaseSandboxSession + + +class Mount(BaseEntry): + is_dir: bool = True + mount_path: Path | None = None + ephemeral: bool = Field(default=True) + + def model_post_init(self, context: object, /) -> None: + _ = context + default_permissions = Permissions( + owner=FileMode.ALL, + group=FileMode.READ | FileMode.EXEC, + other=FileMode.READ | FileMode.EXEC, + ) + if ( + self.permissions.owner != default_permissions.owner + or self.permissions.group != default_permissions.group + or self.permissions.other != default_permissions.other + ): + warnings.warn( + "Mount permissions are not enforced. " + "Please configure access in the cloud provider instead; " + "mount-level permissions can be unreliable.", + stacklevel=2, + ) + self.permissions.owner = default_permissions.owner + self.permissions.group = default_permissions.group + self.permissions.other = default_permissions.other + self.permissions.directory = True + + async def apply( + self, + session: BaseSandboxSession, + dest: Path, + base_dir: Path, + ) -> list[MaterializedFile]: + _ = base_dir + mount_path = self._resolve_mount_path(session, dest) + await self.mount(session, mount_path) + return [] + + async def unmount( + self, + session: BaseSandboxSession, + dest: Path, + base_dir: Path, + ) -> None: + _ = base_dir + mount_path = self._resolve_mount_path(session, dest) + await self.unmount_path(session, mount_path) + + async def mount(self, session: BaseSandboxSession, path: Path) -> None: + await self._mount(session, path) + + async def unmount_path( + self, + session: BaseSandboxSession, + path: Path, + ) -> None: + await self._unmount(session, path) + + @abc.abstractmethod + async def _mount(self, session: BaseSandboxSession, path: Path) -> None: + raise NotImplementedError + + @abc.abstractmethod + async def _unmount(self, session: BaseSandboxSession, path: Path) -> None: + raise NotImplementedError + + def _resolve_mount_path( + self, + session: BaseSandboxSession, + dest: Path, + ) -> Path: + manifest_root = Path(getattr(session.state.manifest, "root", "/")) + if self.mount_path is not None: + mount_path = Path(self.mount_path) + if mount_path.is_absolute(): + return mount_path + # Relative explicit mount paths are interpreted inside the active workspace root so a + # manifest can stay portable across backends with different concrete root prefixes. + return manifest_root / mount_path + + if dest.is_absolute(): + try: + rel_dest = dest.relative_to(manifest_root) + except ValueError: + return dest + # `dest` may already be normalized to an absolute workspace path; re-anchor it to the + # current manifest root instead of nesting the root twice. + return manifest_root / rel_dest + return manifest_root / dest diff --git a/src/agents/sandbox/entries/mounts/patterns.py b/src/agents/sandbox/entries/mounts/patterns.py new file mode 100644 index 0000000000..eef00dd7a0 --- /dev/null +++ b/src/agents/sandbox/entries/mounts/patterns.py @@ -0,0 +1,748 @@ +from __future__ import annotations + +import abc +import io +import re +import shlex +import warnings +from dataclasses import dataclass +from pathlib import Path +from typing import TYPE_CHECKING, Annotated, Literal, TypeVar + +from pydantic import BaseModel, Field + +from ...errors import ( + MountCommandError, + MountConfigError, + MountToolMissingError, + WorkspaceReadNotFoundError, +) + +if TYPE_CHECKING: + from ...session.base_sandbox_session import BaseSandboxSession + + +@dataclass(frozen=True) +class FuseMountConfig: + account: str + container: str + endpoint: str | None + identity_client_id: str | None + account_key: str | None + mount_type: str + + +@dataclass(frozen=True) +class MountpointMountConfig: + bucket: str + access_key_id: str | None + secret_access_key: str | None + session_token: str | None + prefix: str | None + region: str | None + endpoint_url: str | None + mount_type: str + + +@dataclass(frozen=True) +class RcloneMountConfig: + remote_name: str + remote_path: str + remote_kind: str + mount_type: str + config_text: str | None = None + + +MountPatternConfig = FuseMountConfig | MountpointMountConfig | RcloneMountConfig +MountPatternConfigT = TypeVar("MountPatternConfigT", bound=MountPatternConfig) + + +def _require_mount_config( + config: MountPatternConfig, + expected_type: type[MountPatternConfigT], +) -> MountPatternConfigT: + if not isinstance(config, expected_type): + raise MountConfigError( + message="mount pattern received incompatible runtime config", + context={ + "expected": expected_type.__name__, + "actual": type(config).__name__, + }, + ) + return config + + +class MountPatternBase(BaseModel, abc.ABC): + @abc.abstractmethod + async def apply( + self, + session: BaseSandboxSession, + path: Path, + config: MountPatternConfig, + ) -> None: + raise NotImplementedError + + @abc.abstractmethod + async def unapply( + self, + session: BaseSandboxSession, + path: Path, + config: MountPatternConfig, + ) -> None: + raise NotImplementedError + + +class FuseMountPattern(MountPatternBase): + type: Literal["fuse"] = "fuse" + read_only: bool = Field(default=True) + allow_other: bool = Field(default=True) + log_type: str = Field(default="syslog") + log_level: str = Field(default="log_debug") + cache_type: Literal["block_cache", "file_cache"] = Field(default="block_cache") + cache_path: Path | None = None + cache_size_mb: int | None = None + block_cache_block_size_mb: int = Field(default=16) + block_cache_disk_timeout_sec: int = Field(default=3600) + file_cache_timeout_sec: int = Field(default=120) + file_cache_max_size_mb: int | None = None + attr_cache_timeout_sec: int | None = None + entry_cache_timeout_sec: int | None = None + negative_entry_cache_timeout_sec: int | None = None + + @dataclass(frozen=True) + class BlobfuseConfig: + account: str + container: str + endpoint: str + cache_type: str + cache_size_mb: int + block_cache_block_size_mb: int + block_cache_disk_timeout_sec: int + file_cache_timeout_sec: int + file_cache_max_size_mb: int + cache_dir: Path + allow_other: bool + log_type: str + log_level: str + entry_cache_timeout_sec: int | None + negative_entry_cache_timeout_sec: int | None + attr_cache_timeout_sec: int | None + identity_client_id: str | None + account_key: str | None + + def to_text(self) -> str: + lines: list[str] = [] + if self.allow_other: + lines.append("allow-other: true") + lines.append("") + lines.extend( + [ + "logging:", + f" type: {self.log_type}", + f" level: {self.log_level}", + "", + "components:", + " - libfuse", + f" - {self.cache_type}", + " - attr_cache", + " - azstorage", + "", + ] + ) + + libfuse_lines: list[str] = [] + if self.entry_cache_timeout_sec is not None: + libfuse_lines.append(f" entry-expiration-sec: {self.entry_cache_timeout_sec}") + if self.negative_entry_cache_timeout_sec is not None: + libfuse_lines.append( + f" negative-entry-expiration-sec: {self.negative_entry_cache_timeout_sec}" + ) + if libfuse_lines: + lines.append("libfuse:") + lines.extend(libfuse_lines) + lines.append("") + + if self.cache_type == "block_cache": + lines.extend( + [ + "block_cache:", + f" block-size-mb: {self.block_cache_block_size_mb}", + f" mem-size-mb: {self.cache_size_mb}", + f" path: {self.cache_dir}", + f" disk-size-mb: {self.cache_size_mb}", + f" disk-timeout-sec: {self.block_cache_disk_timeout_sec}", + "", + ] + ) + else: + lines.extend( + [ + "file_cache:", + f" path: {self.cache_dir}", + f" timeout-sec: {self.file_cache_timeout_sec}", + f" max-size-mb: {self.file_cache_max_size_mb}", + "", + ] + ) + + attr_cache_timeout = self.attr_cache_timeout_sec or 7200 + lines.extend( + [ + "attr_cache:", + f" timeout-sec: {attr_cache_timeout}", + "", + "azstorage:", + " type: block", + f" account-name: {self.account}", + f" container: {self.container}", + f" endpoint: {self.endpoint}", + ] + ) + if self.account_key: + lines.extend( + [ + " auth-type: key", + f" account-key: {self.account_key}", + ] + ) + else: + lines.append(" mode: msi") + if self.identity_client_id: + lines.append(f" identity-client-id: {self.identity_client_id}") + lines.append("") + return "\n".join(lines) + + async def apply( + self, + session: BaseSandboxSession, + path: Path, + config: MountPatternConfig, + ) -> None: + fuse_config = _require_mount_config(config, FuseMountConfig) + account = fuse_config.account + container = fuse_config.container + + tool_check = await session.exec("command -v blobfuse2 >/dev/null 2>&1") + if not tool_check.ok(): + raise MountToolMissingError( + tool="blobfuse2", + context={"account": account, "container": container}, + ) + + session_id = getattr(session.state, "session_id", None) + if session_id is None: + raise MountConfigError( + message="mount session is missing session_id", + context={"type": fuse_config.mount_type}, + ) + + mount_path = path + cache_dir = ( + Path(self.cache_path) + if self.cache_path is not None + else Path(f"/tmp/uc-blobfuse-cache/{session_id.hex}") / account / container + ) + config_dir = Path(f"/tmp/uc-blobfuse-config/{session_id.hex}") + config_name = f"{account}_{container}".replace("/", "_") + config_path = config_dir / f"{config_name}.yaml" + + await session.mkdir(mount_path, parents=True) + await session.mkdir(cache_dir, parents=True) + await session.mkdir(config_dir, parents=True) + + endpoint = fuse_config.endpoint or f"https://{account}.blob.core.windows.net" + cache_type = self.cache_type + cache_size_mb = self.cache_size_mb or (50_000 if cache_type == "block_cache" else 4_096) + file_cache_max_size_mb = self.file_cache_max_size_mb or cache_size_mb + blobfuse_config = self.BlobfuseConfig( + account=account, + container=container, + endpoint=endpoint, + cache_type=cache_type, + cache_size_mb=cache_size_mb, + block_cache_block_size_mb=self.block_cache_block_size_mb, + block_cache_disk_timeout_sec=self.block_cache_disk_timeout_sec, + file_cache_timeout_sec=self.file_cache_timeout_sec, + file_cache_max_size_mb=file_cache_max_size_mb, + cache_dir=cache_dir, + allow_other=self.allow_other, + log_type=self.log_type, + log_level=self.log_level, + entry_cache_timeout_sec=self.entry_cache_timeout_sec, + negative_entry_cache_timeout_sec=self.negative_entry_cache_timeout_sec, + attr_cache_timeout_sec=self.attr_cache_timeout_sec, + identity_client_id=fuse_config.identity_client_id, + account_key=fuse_config.account_key, + ) + config_payload = blobfuse_config.to_text().encode("utf-8") + await session.write(config_path, io.BytesIO(config_payload)) + + cmd: list[str] = ["blobfuse2", "mount"] + if self.read_only: + cmd.append("--read-only") + cmd.extend(["--config-file", str(config_path)]) + cmd.append(str(mount_path)) + + result = await session.exec(*cmd, shell=False) + if not result.ok(): + raise MountCommandError( + command=" ".join(cmd), + stderr=result.stderr.decode("utf-8", errors="replace"), + context={"account": account, "container": container}, + ) + + async def unapply( + self, + session: BaseSandboxSession, + path: Path, + config: MountPatternConfig, + ) -> None: + _ = _require_mount_config(config, FuseMountConfig) + # Best-effort unmount; ignore failures for already-unmounted mounts. + await session.exec( + "sh", + "-lc", + f"fusermount3 -u {shlex.quote(str(path))} || umount {shlex.quote(str(path))}", + shell=False, + ) + + +class MountpointMountPattern(MountPatternBase): + type: Literal["mountpoint"] = "mountpoint" + read_only: bool = Field(default=True) + + @dataclass(frozen=True) + class MountpointOptions: + prefix: str | None = None + region: str | None = None + endpoint_url: str | None = None + + options: MountpointOptions = Field(default_factory=MountpointOptions) + + async def apply( + self, + session: BaseSandboxSession, + path: Path, + config: MountPatternConfig, + ) -> None: + mountpoint_config = _require_mount_config(config, MountpointMountConfig) + bucket = mountpoint_config.bucket + + tool_check = await session.exec("command -v mount-s3 >/dev/null 2>&1") + if not tool_check.ok(): + raise MountToolMissingError( + tool="mount-s3", + context={"bucket": bucket}, + ) + + await session.mkdir(path, parents=True) + + cmd: list[str] = ["mount-s3"] + if self.read_only: + cmd.append("--read-only") + if mountpoint_config.region: + cmd.extend(["--region", mountpoint_config.region]) + if mountpoint_config.endpoint_url: + cmd.extend(["--endpoint-url", mountpoint_config.endpoint_url]) + if mountpoint_config.prefix: + cmd.extend(["--prefix", mountpoint_config.prefix]) + cmd.extend([bucket, str(path)]) + + env_parts: list[str] = [] + access_key_id = mountpoint_config.access_key_id + secret_access_key = mountpoint_config.secret_access_key + session_token = mountpoint_config.session_token + if access_key_id and secret_access_key: + env_parts.append(f"AWS_ACCESS_KEY_ID={shlex.quote(access_key_id)}") + env_parts.append(f"AWS_SECRET_ACCESS_KEY={shlex.quote(secret_access_key)}") + if session_token: + env_parts.append(f"AWS_SESSION_TOKEN={shlex.quote(session_token)}") + + joined_cmd = " ".join(shlex.quote(part) for part in cmd) + if env_parts: + joined_cmd = f"{' '.join(env_parts)} {joined_cmd}" + + result = await session.exec("sh", "-lc", joined_cmd, shell=False) + if not result.ok(): + raise MountCommandError( + command=joined_cmd, + stderr=result.stderr.decode("utf-8", errors="replace"), + context={"bucket": bucket}, + ) + + async def unapply( + self, + session: BaseSandboxSession, + path: Path, + config: MountPatternConfig, + ) -> None: + _ = _require_mount_config(config, MountpointMountConfig) + await session.exec( + "sh", + "-lc", + f"fusermount3 -u {shlex.quote(str(path))} || umount {shlex.quote(str(path))}", + shell=False, + ) + + +def _supplement_rclone_config_text( + *, + config_text: str, + remote_name: str, + required_lines: list[str], + mount_type: str | None, +) -> str: + section_pattern = re.compile(rf"^\s*\[{re.escape(remote_name)}\]\s*$", re.MULTILINE) + match = section_pattern.search(config_text) + if not match: + raise MountConfigError( + message="rclone config missing required remote section", + context={"type": mount_type or "mount", "remote_name": remote_name}, + ) + + section_start = match.start() + section_end = match.end() + next_section = re.search(r"^\s*\[.+\]\s*$", config_text[section_end:], re.MULTILINE) + if next_section: + section_body_end = section_end + next_section.start() + else: + section_body_end = len(config_text) + + before = config_text[:section_start] + section_body = config_text[section_start:section_body_end].rstrip("\n") + after = config_text[section_body_end:] + + supplement = "\n".join(required_lines[1:]) # header already present + merged_section = f"{section_body}\n{supplement}\n" + return f"{before}{merged_section}{after}" + + +class RcloneMountPattern(MountPatternBase): + type: Literal["rclone"] = "rclone" + mode: Literal["fuse", "nfs"] = Field(default="fuse") + read_only: bool = Field(default=True) + remote_name: str | None = None + extra_args: list[str] = Field(default_factory=list) + nfs_addr: str | None = None + nfs_mount_options: list[str] | None = None + config_file_path: Path | None = None + + def resolve_remote_name( + self, + *, + session_id: str, + remote_kind: str, + mount_type: str | None = None, + ) -> str: + if self.remote_name: + return self.remote_name + if not remote_kind: + raise MountConfigError( + message="rclone mount requires remote_kind", + context={"type": mount_type or "mount"}, + ) + # Derive a deterministic per-session remote name when the caller did not pin one, so + # multiple mounts can coexist without sharing mutable rclone config sections. + return f"uc_{remote_kind}_{session_id}" + + def _resolve_config_path( + self, + session: BaseSandboxSession, + config_path: Path, + ) -> Path: + manifest_root = Path(getattr(session.state.manifest, "root", "/")) + if config_path.is_absolute(): + return config_path + # Relative config paths are resolved inside the sandbox workspace, not relative to the + # host process that is orchestrating the session. + return manifest_root / config_path + + async def read_config_text( + self, + session: BaseSandboxSession, + remote_name: str, + *, + mount_type: str | None, + ) -> str: + if self.config_file_path is None: + raise MountConfigError( + message="rclone config_file_path is not set", + context={"type": mount_type or "mount"}, + ) + config_path = self._resolve_config_path(session, self.config_file_path) + try: + handle = await session.read(config_path) + except WorkspaceReadNotFoundError: + raise + except FileNotFoundError as e: + raise WorkspaceReadNotFoundError(path=config_path, cause=e) from e + except Exception as e: + raise MountConfigError( + message="failed to read rclone config file", + context={"type": mount_type or "mount", "path": str(config_path)}, + ) from e + + try: + raw_config = handle.read() + finally: + handle.close() + if isinstance(raw_config, bytes): + config_text = raw_config.decode("utf-8", errors="replace") + elif isinstance(raw_config, str): + config_text = raw_config + else: + config_text = str(raw_config) + + if not config_text.strip(): + raise MountConfigError( + message="rclone config file is empty", + context={"type": mount_type or "mount", "path": str(config_path)}, + ) + + section_pattern = rf"^\s*\[{re.escape(remote_name)}\]\s*$" + if not re.search(section_pattern, config_text, re.MULTILINE): + raise MountConfigError( + message="rclone config missing required remote section", + context={ + "type": mount_type or "mount", + "path": str(config_path), + "remote_name": remote_name, + }, + ) + + return config_text + + async def _start_rclone_server( + self, + session: BaseSandboxSession, + *, + config: RcloneMountConfig, + config_path: Path, + nfs_addr: str, + ) -> None: + nfs_check = await session.exec( + "sh", + "-lc", + "/usr/local/bin/rclone serve nfs --help >/dev/null 2>&1" + " || rclone serve nfs --help >/dev/null 2>&1", + shell=False, + ) + if not nfs_check.ok(): + raise MountToolMissingError( + tool="rclone serve nfs", + context={"type": config.mount_type}, + ) + cmd: list[str] = ["rclone", "serve", "nfs", f"{config.remote_name}:{config.remote_path}"] + cmd.extend(["--addr", nfs_addr]) + cmd.extend(["--config", str(config_path)]) + if self.extra_args: + cmd.extend(self.extra_args) + joined_cmd = " ".join(shlex.quote(part) for part in cmd) + # Run in background so we can wait for the server to start. + server_cmd = f"{joined_cmd} &" + result = await session.exec("sh", "-lc", server_cmd, shell=False) + if not result.ok(): + raise MountCommandError( + command=" ".join(cmd), + stderr=result.stderr.decode("utf-8", errors="replace"), + context={"type": config.mount_type}, + ) + + async def _start_rclone_client( + self, + session: BaseSandboxSession, + *, + path: Path, + config: RcloneMountConfig, + config_path: Path, + nfs_addr: str | None = None, + ) -> None: + if self.mode == "fuse": + cmd: list[str] = [ + "rclone", + "mount", + f"{config.remote_name}:{config.remote_path}", + str(path), + ] + if self.read_only: + cmd.append("--read-only") + cmd.extend(["--config", str(config_path), "--daemon"]) + if self.extra_args: + cmd.extend(self.extra_args) + result = await session.exec(*cmd, shell=False) + if not result.ok(): + raise MountCommandError( + command=" ".join(cmd), + stderr=result.stderr.decode("utf-8", errors="replace"), + context={"type": config.mount_type}, + ) + return + + if nfs_addr is None: + raise MountConfigError( + message="nfs_addr required for rclone nfs client", + context={"type": config.mount_type}, + ) + + nfs_supported = await session.exec( + "sh", "-lc", "grep -w nfs /proc/filesystems", shell=False + ) + if not nfs_supported.ok(): + warnings.warn( + "NFS client support not detected; attempting mount anyway. " + "If it fails, use rclone fuse mode or run on a kernel with NFS support.", + stacklevel=2, + ) + + # Default to localhost if no NFS address is provided + host = "127.0.0.1" + port = "2049" + + if ":" in nfs_addr: + host, port = nfs_addr.rsplit(":", 1) + else: + host = nfs_addr + if host in {"0.0.0.0", "::"}: + host = "127.0.0.1" + + mount_options = self.nfs_mount_options or [ + "vers=4.1", + "tcp", + f"port={port}", + "soft", + "timeo=50", + "retrans=1", + ] + option_arg = ",".join(mount_options) + timeout_check = await session.exec( + "sh", "-lc", "command -v timeout >/dev/null 2>&1", shell=False + ) + timeout_prefix = "timeout 10s " if timeout_check.ok() else "" + mount_cmd_string = " ".join( + [ + "for i in 1 2 3; do", + f"{timeout_prefix}mount", + "-v", + "-t", + "nfs", + "-o", + shlex.quote(option_arg), + f"{shlex.quote(host)}:/", + shlex.quote(str(path)), + "&& exit 0; sleep 1; done; exit 1", + ] + ) + mount_cmd = ( + "sh", + "-lc", + mount_cmd_string, + ) + mount_result = await session.exec(*mount_cmd, shell=False) + if not mount_result.ok(): + raise MountCommandError( + command=" ".join(mount_cmd), + stderr=mount_result.stderr.decode("utf-8", errors="replace"), + context={"type": config.mount_type}, + ) + + async def apply( + self, + session: BaseSandboxSession, + path: Path, + config: MountPatternConfig, + ) -> None: + rclone_config = _require_mount_config(config, RcloneMountConfig) + tool_check = await session.exec( + "sh", + "-lc", + "command -v rclone >/dev/null 2>&1 || test -x /usr/local/bin/rclone", + shell=False, + ) + if not tool_check.ok(): + raise MountToolMissingError( + tool="rclone", + context={"type": rclone_config.mount_type}, + ) + + if rclone_config.config_text is None: + raise MountConfigError( + message="rclone mount requires config_text", + context={"type": rclone_config.mount_type}, + ) + + session_id = getattr(session.state, "session_id", None) + if session_id is None: + raise MountConfigError( + message="mount session is missing session_id", + context={"type": rclone_config.mount_type}, + ) + session_id_str = session_id.hex + config_dir = Path(f"/tmp/uc-rclone-config/{session_id_str}") + config_path = config_dir / f"{rclone_config.remote_name}.conf" + await session.mkdir(path, parents=True) + await session.mkdir(config_dir, parents=True) + # Always write an isolated config file for the live mount operation so provider-specific + # augmentation does not mutate a shared source config in the workspace. + await session.write(config_path, io.BytesIO(rclone_config.config_text.encode("utf-8"))) + + if self.mode == "nfs": + nfs_addr = self.nfs_addr or "127.0.0.1:2049" + await self._start_rclone_server( + session, + config=rclone_config, + config_path=config_path, + nfs_addr=nfs_addr, + ) + await self._start_rclone_client( + session, + path=path, + config=rclone_config, + config_path=config_path, + nfs_addr=nfs_addr, + ) + else: + # fuse mode + await self._start_rclone_client( + session, + path=path, + config=rclone_config, + config_path=config_path, + ) + + async def unapply( + self, + session: BaseSandboxSession, + path: Path, + config: MountPatternConfig, + ) -> None: + rclone_config = _require_mount_config(config, RcloneMountConfig) + if self.mode == "fuse": + await session.exec( + "sh", + "-lc", + f"fusermount3 -u {shlex.quote(str(path))} || umount {shlex.quote(str(path))}", + shell=False, + ) + if self.mode == "nfs": + await session.exec( + "sh", + "-lc", + f"umount {shlex.quote(str(path))} >/dev/null 2>&1 || true", + shell=False, + ) + + await session.exec( + "sh", + "-lc", + ( + "pkill -f -- " + f"'rclone (mount|serve nfs) {rclone_config.remote_name}:' >/dev/null 2>&1 || true" + ), + shell=False, + ) + + +MountPattern = Annotated[ + FuseMountPattern | MountpointMountPattern | RcloneMountPattern, + Field(discriminator="type"), +] diff --git a/src/agents/sandbox/entries/mounts/providers.py b/src/agents/sandbox/entries/mounts/providers.py new file mode 100644 index 0000000000..8cba989eb4 --- /dev/null +++ b/src/agents/sandbox/entries/mounts/providers.py @@ -0,0 +1,320 @@ +from __future__ import annotations + +import abc +import uuid +from pathlib import Path +from typing import TYPE_CHECKING, Literal + +from ...errors import MountConfigError +from .base import Mount +from .patterns import ( + FuseMountConfig, + FuseMountPattern, + MountPattern, + MountPatternConfig, + MountpointMountConfig, + MountpointMountPattern, + RcloneMountConfig, + RcloneMountPattern, + _supplement_rclone_config_text, +) + +if TYPE_CHECKING: + from ...session.base_sandbox_session import BaseSandboxSession + + +class _ConfiguredMount(Mount, abc.ABC): + mount_pattern: MountPattern | None = None + + def _require_mount_pattern(self) -> MountPattern: + if self.mount_pattern is None: + raise MountConfigError( + message=f"{self.type} requires mount_pattern", + context={"type": self.type}, + ) + return self.mount_pattern + + @staticmethod + def _require_session_id_hex(session: BaseSandboxSession, mount_type: str) -> str: + session_id = getattr(session.state, "session_id", None) + if not isinstance(session_id, uuid.UUID): + raise MountConfigError( + message="mount session is missing session_id", + context={"type": mount_type}, + ) + return session_id.hex + + async def _build_rclone_config( + self, + *, + session: BaseSandboxSession, + pattern: RcloneMountPattern, + remote_kind: str, + remote_path: str, + required_lines: list[str], + include_config_text: bool, + ) -> RcloneMountConfig: + remote_name = pattern.resolve_remote_name( + session_id=self._require_session_id_hex(session, self.type), + remote_kind=remote_kind, + mount_type=self.type, + ) + config_text: str | None = None + if include_config_text: + if pattern.config_file_path is not None: + config_text = await pattern.read_config_text( + session, + remote_name, + mount_type=self.type, + ) + config_text = _supplement_rclone_config_text( + config_text=config_text, + remote_name=remote_name, + required_lines=required_lines, + mount_type=self.type, + ) + else: + config_text = "\n".join(required_lines) + "\n" + return RcloneMountConfig( + remote_name=remote_name, + remote_path=remote_path, + remote_kind=remote_kind, + mount_type=self.type, + config_text=config_text, + ) + + @abc.abstractmethod + async def _build_mount_config( + self, + session: BaseSandboxSession, + pattern: MountPattern, + *, + include_config_text: bool, + ) -> MountPatternConfig: + raise NotImplementedError + + async def _mount(self, session: BaseSandboxSession, path: Path) -> None: + pattern = self._require_mount_pattern() + config = await self._build_mount_config(session, pattern, include_config_text=True) + await pattern.apply(session, path, config) + + async def _unmount(self, session: BaseSandboxSession, path: Path) -> None: + pattern = self._require_mount_pattern() + config = await self._build_mount_config(session, pattern, include_config_text=False) + await pattern.unapply(session, path, config) + + +class AzureBlobMount(_ConfiguredMount): + type: Literal["azure_blob_mount"] = "azure_blob_mount" + account: str # AZURE_STORAGE_ACCOUNT + container: str # AZURE_STORAGE_CONTAINER + endpoint: str | None = None + identity_client_id: str | None = None # AZURE_CLIENT_ID + account_key: str | None = None # AZURE_STORAGE_ACCOUNT_KEY + + def model_post_init(self, context: object, /) -> None: + super().model_post_init(context) + pattern = self._require_mount_pattern() + if not isinstance(pattern, (RcloneMountPattern, FuseMountPattern)): + raise MountConfigError( + message="invalid mount_pattern type", + context={"type": self.type}, + ) + + async def _build_mount_config( + self, + session: BaseSandboxSession, + pattern: MountPattern, + *, + include_config_text: bool, + ) -> MountPatternConfig: + if isinstance(pattern, RcloneMountPattern): + return await self._build_rclone_config( + session=session, + pattern=pattern, + remote_kind="azureblob", + remote_path=self.container, + required_lines=self._rclone_required_lines( + pattern.resolve_remote_name( + session_id=self._require_session_id_hex(session, self.type), + remote_kind="azureblob", + mount_type=self.type, + ) + ), + include_config_text=include_config_text, + ) + if isinstance(pattern, FuseMountPattern): + return FuseMountConfig( + account=self.account, + container=self.container, + endpoint=self.endpoint, + identity_client_id=self.identity_client_id, + account_key=self.account_key, + mount_type=self.type, + ) + raise MountConfigError( + message="invalid mount_pattern type", + context={"type": self.type}, + ) + + def _rclone_required_lines(self, remote_name: str) -> list[str]: + lines = [ + f"[{remote_name}]", + "type = azureblob", + f"account = {self.account}", + ] + if self.endpoint: + lines.append(f"endpoint = {self.endpoint}") + if self.account_key: + lines.append(f"key = {self.account_key}") + else: + lines.append("use_msi = true") + if self.identity_client_id: + lines.append(f"msi_client_id = {self.identity_client_id}") + return lines + + +class S3Mount(_ConfiguredMount): + type: Literal["s3_mount"] = "s3_mount" + bucket: str + access_key_id: str | None = None + secret_access_key: str | None = None + session_token: str | None = None + + def model_post_init(self, context: object, /) -> None: + super().model_post_init(context) + pattern = self._require_mount_pattern() + if not isinstance(pattern, (RcloneMountPattern, MountpointMountPattern)): + raise MountConfigError( + message="invalid mount_pattern type", + context={"type": self.type}, + ) + + async def _build_mount_config( + self, + session: BaseSandboxSession, + pattern: MountPattern, + *, + include_config_text: bool, + ) -> MountPatternConfig: + if isinstance(pattern, RcloneMountPattern): + return await self._build_rclone_config( + session=session, + pattern=pattern, + remote_kind="s3", + remote_path=self.bucket, + required_lines=self._rclone_required_lines( + pattern.resolve_remote_name( + session_id=self._require_session_id_hex(session, self.type), + remote_kind="s3", + mount_type=self.type, + ) + ), + include_config_text=include_config_text, + ) + if isinstance(pattern, MountpointMountPattern): + options = pattern.options + return MountpointMountConfig( + bucket=self.bucket, + access_key_id=self.access_key_id, + secret_access_key=self.secret_access_key, + session_token=self.session_token, + prefix=options.prefix, + region=options.region, + endpoint_url=options.endpoint_url, + mount_type=self.type, + ) + raise MountConfigError( + message="invalid mount_pattern type", + context={"type": self.type}, + ) + + def _rclone_required_lines(self, remote_name: str) -> list[str]: + lines = [ + f"[{remote_name}]", + "type = s3", + "provider = AWS", + ] + if self.access_key_id and self.secret_access_key: + lines.append("env_auth = false") + lines.append(f"access_key_id = {self.access_key_id}") + lines.append(f"secret_access_key = {self.secret_access_key}") + if self.session_token: + lines.append(f"session_token = {self.session_token}") + else: + lines.append("env_auth = true") + return lines + + +class GCSMount(_ConfiguredMount): + type: Literal["gcs_mount"] = "gcs_mount" + bucket: str + access_id: str | None = None + secret_access_key: str | None = None + + def model_post_init(self, context: object, /) -> None: + super().model_post_init(context) + if self.mount_pattern is None: + # GCS defaults to the S3-compatible mountpoint path so examples can omit the pattern + # unless they specifically need rclone behavior. + self.mount_pattern = MountpointMountPattern() + pattern = self._require_mount_pattern() + if not isinstance(pattern, (RcloneMountPattern, MountpointMountPattern)): + raise MountConfigError( + message="invalid mount_pattern type", + context={"type": self.type}, + ) + + async def _build_mount_config( + self, + session: BaseSandboxSession, + pattern: MountPattern, + *, + include_config_text: bool, + ) -> MountPatternConfig: + if isinstance(pattern, RcloneMountPattern): + return await self._build_rclone_config( + session=session, + pattern=pattern, + remote_kind="gcs", + remote_path=self.bucket, + required_lines=self._rclone_required_lines( + pattern.resolve_remote_name( + session_id=self._require_session_id_hex(session, self.type), + remote_kind="gcs", + mount_type=self.type, + ) + ), + include_config_text=include_config_text, + ) + if isinstance(pattern, MountpointMountPattern): + options = pattern.options + return MountpointMountConfig( + bucket=self.bucket, + access_key_id=self.access_id, + secret_access_key=self.secret_access_key, + session_token=None, + prefix=options.prefix, + region=options.region, + endpoint_url=options.endpoint_url or "https://storage.googleapis.com", + mount_type=self.type, + ) + raise MountConfigError( + message="invalid mount_pattern type", + context={"type": self.type}, + ) + + def _rclone_required_lines(self, remote_name: str) -> list[str]: + lines = [ + f"[{remote_name}]", + "type = s3", + "provider = GCS", + "endpoint = https://storage.googleapis.com", + ] + if self.access_id and self.secret_access_key: + lines.append("env_auth = false") + lines.append(f"access_key_id = {self.access_id}") + lines.append(f"secret_access_key = {self.secret_access_key}") + else: + lines.append("env_auth = true") + return lines diff --git a/src/agents/sandbox/errors.py b/src/agents/sandbox/errors.py new file mode 100644 index 0000000000..b2884d8b3e --- /dev/null +++ b/src/agents/sandbox/errors.py @@ -0,0 +1,649 @@ +from __future__ import annotations + +from collections.abc import Mapping, Sequence +from dataclasses import dataclass +from enum import Enum +from pathlib import Path +from typing import Literal + +from .types import ExecResult + + +class ErrorCode(str, Enum): + """Stable, machine-readable error codes for `UniversalComputerError`.""" + + def __str__(self) -> str: + return str(self.value) + + INVALID_MANIFEST_PATH = "invalid_manifest_path" + INVALID_COMPRESSION_SCHEME = "invalid_compression_scheme" + EXEC_NONZERO = "exec_nonzero" + EXEC_TIMEOUT = "exec_timeout" + EXEC_TRANSPORT_ERROR = "exec_transport_error" + + WORKSPACE_READ_NOT_FOUND = "workspace_read_not_found" + WORKSPACE_ARCHIVE_READ_ERROR = "workspace_archive_read_error" + WORKSPACE_ARCHIVE_WRITE_ERROR = "workspace_archive_write_error" + WORKSPACE_WRITE_TYPE_ERROR = "workspace_write_type_error" + WORKSPACE_STOP_ERROR = "workspace_stop_error" + WORKSPACE_START_ERROR = "workspace_start_error" + WORKSPACE_ROOT_NOT_FOUND = "workspace_root_not_found" + + LOCAL_FILE_READ_ERROR = "local_file_read_error" + LOCAL_DIR_READ_ERROR = "local_dir_read_error" + LOCAL_CHECKSUM_ERROR = "local_checksum_error" + + GIT_MISSING_IN_IMAGE = "git_missing_in_image" + GIT_CLONE_ERROR = "git_clone_error" + GIT_COPY_ERROR = "git_copy_error" + + MOUNT_MISSING_TOOL = "mount_missing_tool" + MOUNT_FAILED = "mount_failed" + MOUNT_CONFIG_INVALID = "mount_config_invalid" + + SNAPSHOT_PERSIST_ERROR = "snapshot_persist_error" + SNAPSHOT_RESTORE_ERROR = "snapshot_restore_error" + SNAPSHOT_NOT_RESTORABLE = "snapshot_not_restorable" + + +OpName = Literal[ + "start", + "stop", + "exec", + "read", + "write", + "shutdown", + "running", + "persist_workspace", + "hydrate_workspace", + "materialize", + "snapshot_persist", + "snapshot_restore", +] + + +@dataclass(eq=False) +class UniversalComputerError(Exception): + """Base class for structured, user-facing sandbox errors. + + Attributes: + message: Human-readable error message. + error_code: Stable, machine-readable code for programmatic handling. + op: The operation where the error occurred. + context: Structured metadata to aid debugging. + cause: Optional underlying exception. + """ + + message: str + error_code: ErrorCode + op: OpName + context: dict[str, object] + cause: BaseException | None = None + + def __post_init__(self) -> None: + super().__init__(self.message) + if self.cause is not None: + self.__cause__ = self.cause + + @property + def code(self) -> str: + """Backward-compatible alias for `error_code`.""" + + return str(self.error_code) + + +class ConfigurationError(UniversalComputerError): + """Raised when validating user-provided configuration and inputs.""" + + +class SandboxError(UniversalComputerError): + """Raised for sandbox failures (e.g., Docker/IO/transport).""" + + +class ArtifactError(UniversalComputerError): + """Raised while materializing input artifacts (local files, git repos).""" + + +class SnapshotError(UniversalComputerError): + """Raised for snapshot persist/restore errors.""" + + +def _as_context(context: Mapping[str, object] | None) -> dict[str, object]: + return dict(context or {}) + + +def _format_command(command: Sequence[str | Path]) -> str: + return " ".join(str(p) for p in command) + + +class InvalidManifestPathError(ConfigurationError): + """Manifest path was invalid (absolute or escaped the workspace root).""" + + def __init__( + self, + *, + rel: str | Path, + reason: Literal["absolute", "escape_root"], + context: Mapping[str, object] | None = None, + cause: BaseException | None = None, + ) -> None: + msg = ( + f"manifest path must be relative: {rel}" + if reason == "absolute" + else f"manifest path must not escape root: {rel}" + ) + super().__init__( + message=msg, + error_code=ErrorCode.INVALID_MANIFEST_PATH, + op="materialize", + context={"rel": str(rel), "reason": reason, **_as_context(context)}, + cause=cause, + ) + + +class InvalidCompressionSchemeError(ConfigurationError): + """Compression scheme was missing or unsupported for a workspace write.""" + + def __init__( + self, + *, + path: Path, + scheme: str | None, + context: Mapping[str, object] | None = None, + cause: BaseException | None = None, + ) -> None: + msg = ( + "could not determine compression scheme" + if not scheme + else "compression scheme must be one of 'zip' 'tar'" + ) + super().__init__( + message=msg, + error_code=ErrorCode.INVALID_COMPRESSION_SCHEME, + op="write", + context={"path": str(path), "scheme": scheme, **_as_context(context)}, + cause=cause, + ) + + +class ExecFailureError(SandboxError): + """Base class for exec()-related failures.""" + + command: tuple[str, ...] + + def __init__( + self, + *, + message: str, + error_code: ErrorCode, + command: Sequence[str | Path], + context: Mapping[str, object] | None = None, + cause: BaseException | None = None, + ) -> None: + cmd = tuple(str(c) for c in command) + super().__init__( + message=message, + error_code=error_code, + op="exec", + context={"command": cmd, "command_str": _format_command(cmd), **_as_context(context)}, + cause=cause, + ) + self.command = cmd + + +class ExecNonZeroError(ExecFailureError): + """exec() returned a non-zero exit status.""" + + exit_code: int + stdout: bytes + stderr: bytes + + def __init__( + self, + exec_result: ExecResult, + *, + command: Sequence[str | Path], + context: Mapping[str, object] | None = None, + cause: BaseException | None = None, + ) -> None: + super().__init__( + message=exec_result.stderr.decode("utf-8", errors="replace"), + error_code=ErrorCode.EXEC_NONZERO, + command=command, + context={ + "exit_code": exec_result.exit_code, + **_as_context(context), + }, + cause=cause, + ) + self.exit_code = exec_result.exit_code + self.stdout = exec_result.stdout + self.stderr = exec_result.stderr + + +class ExecTimeoutError(ExecFailureError): + """exec() exceeded its timeout.""" + + timeout_s: float | None + + def __init__( + self, + *, + command: Sequence[str | Path], + timeout_s: float | None, + context: Mapping[str, object] | None = None, + cause: BaseException | None = None, + ) -> None: + super().__init__( + message="command timed out", + error_code=ErrorCode.EXEC_TIMEOUT, + command=command, + context={"timeout_s": timeout_s, **_as_context(context)}, + cause=cause, + ) + self.timeout_s = timeout_s + + +class ExecTransportError(ExecFailureError): + """exec() failed due to a transport-level error (e.g., Docker API).""" + + def __init__( + self, + *, + command: Sequence[str | Path], + context: Mapping[str, object] | None = None, + cause: BaseException | None = None, + ) -> None: + super().__init__( + message="exec transport error", + error_code=ErrorCode.EXEC_TRANSPORT_ERROR, + command=command, + context=_as_context(context), + cause=cause, + ) + + +class WorkspaceIOError(SandboxError): + """Base class for workspace read/write errors.""" + + +class WorkspaceReadNotFoundError(WorkspaceIOError): + """Workspace read failed because the path does not exist.""" + + def __init__( + self, + *, + path: Path, + context: Mapping[str, object] | None = None, + cause: BaseException | None = None, + ) -> None: + super().__init__( + message=f"file not found: {path}", + error_code=ErrorCode.WORKSPACE_READ_NOT_FOUND, + op="read", + context={"path": str(path), **_as_context(context)}, + cause=cause, + ) + + +class WorkspaceArchiveReadError(WorkspaceIOError): + """Workspace read failed while reading or decoding the archive stream.""" + + def __init__( + self, + *, + path: Path, + context: Mapping[str, object] | None = None, + cause: BaseException | None = None, + ) -> None: + super().__init__( + message=f"failed to read archive for path: {path}", + error_code=ErrorCode.WORKSPACE_ARCHIVE_READ_ERROR, + op="read", + context={"path": str(path), **_as_context(context)}, + cause=cause, + ) + + +class WorkspaceArchiveWriteError(WorkspaceIOError): + """Workspace write failed while creating or sending the archive stream.""" + + def __init__( + self, + *, + path: Path, + context: Mapping[str, object] | None = None, + cause: BaseException | None = None, + ) -> None: + super().__init__( + message=f"failed to write archive for path: {path}", + error_code=ErrorCode.WORKSPACE_ARCHIVE_WRITE_ERROR, + op="write", + context={"path": str(path), **_as_context(context)}, + cause=cause, + ) + + +class WorkspaceWriteTypeError(WorkspaceIOError): + """Workspace write payload was not a binary file-like object.""" + + def __init__( + self, + *, + path: Path, + actual_type: str, + context: Mapping[str, object] | None = None, + cause: BaseException | None = None, + ) -> None: + super().__init__( + message="write() expects a binary file-like object", + error_code=ErrorCode.WORKSPACE_WRITE_TYPE_ERROR, + op="write", + context={"path": str(path), "actual_type": actual_type, **_as_context(context)}, + cause=cause, + ) + + +class WorkspaceStopError(SandboxError): + """SandboxSession stop failed (typically during snapshot persistence).""" + + def __init__( + self, + *, + path: Path, + context: Mapping[str, object] | None = None, + cause: BaseException | None = None, + ) -> None: + super().__init__( + message="failed to stop session", + error_code=ErrorCode.WORKSPACE_STOP_ERROR, + op="stop", + context={"path": str(path), **_as_context(context)}, + cause=cause, + ) + + +class WorkspaceStartError(SandboxError): + """SandboxSession start failed (typically while ensuring the workspace root exists).""" + + def __init__( + self, + *, + path: Path, + context: Mapping[str, object] | None = None, + cause: BaseException | None = None, + ) -> None: + super().__init__( + message="failed to start session", + error_code=ErrorCode.WORKSPACE_START_ERROR, + op="start", + context={"path": str(path), **_as_context(context)}, + cause=cause, + ) + + +class WorkspaceRootNotFoundError(SandboxError): + """Workspace root is missing on disk (e.g. deleted mid-session).""" + + def __init__( + self, + *, + path: Path, + context: Mapping[str, object] | None = None, + cause: BaseException | None = None, + ) -> None: + super().__init__( + message=f"workspace root not found: {path}", + error_code=ErrorCode.WORKSPACE_ROOT_NOT_FOUND, + op="exec", + context={"path": str(path), **_as_context(context)}, + cause=cause, + ) + + +class LocalArtifactError(ArtifactError): + """Base class for errors while reading local artifacts.""" + + +class LocalFileReadError(LocalArtifactError): + """Failed to read a local file artifact from disk.""" + + def __init__( + self, + *, + src: Path, + context: Mapping[str, object] | None = None, + cause: BaseException | None = None, + ) -> None: + super().__init__( + message=f"failed to read local file artifact: {src}", + error_code=ErrorCode.LOCAL_FILE_READ_ERROR, + op="materialize", + context={"src": str(src), **_as_context(context)}, + cause=cause, + ) + + +class LocalDirReadError(LocalArtifactError): + """Failed to read a local directory artifact from disk.""" + + def __init__( + self, + *, + src: Path, + context: Mapping[str, object] | None = None, + cause: BaseException | None = None, + ) -> None: + super().__init__( + message=f"failed to read local dir artifact: {src}", + error_code=ErrorCode.LOCAL_DIR_READ_ERROR, + op="materialize", + context={"src": str(src), **_as_context(context)}, + cause=cause, + ) + + +class LocalChecksumError(LocalArtifactError): + """Failed to compute a checksum for a local artifact.""" + + def __init__( + self, + *, + src: Path, + context: Mapping[str, object] | None = None, + cause: BaseException | None = None, + ) -> None: + super().__init__( + message=f"failed to checksum local artifact: {src}", + error_code=ErrorCode.LOCAL_CHECKSUM_ERROR, + op="materialize", + context={"src": str(src), **_as_context(context)}, + cause=cause, + ) + + +class GitArtifactError(ArtifactError): + """Base class for errors while materializing git_repo artifacts.""" + + +class GitMissingInImageError(GitArtifactError): + """Container image is missing git, so git_repo artifacts cannot be materialized.""" + + def __init__( + self, + *, + context: Mapping[str, object] | None = None, + cause: BaseException | None = None, + ) -> None: + super().__init__( + message="git is required in the container image to materialize git_repo artifacts", + error_code=ErrorCode.GIT_MISSING_IN_IMAGE, + op="materialize", + context=_as_context(context), + cause=cause, + ) + + +class GitCloneError(GitArtifactError): + """Failed to clone a git repository while materializing an artifact.""" + + def __init__( + self, + *, + url: str, + ref: str, + stderr: str | None = None, + context: Mapping[str, object] | None = None, + cause: BaseException | None = None, + ) -> None: + super().__init__( + message=f"git clone failed for {url}@{ref}", + error_code=ErrorCode.GIT_CLONE_ERROR, + op="materialize", + context={"url": url, "ref": ref, "stderr": stderr, **_as_context(context)}, + cause=cause, + ) + + +class GitCopyError(GitArtifactError): + """Failed to copy files from a cloned repo into the workspace.""" + + def __init__( + self, + *, + src_root: str, + dest: Path, + stderr: str | None = None, + context: Mapping[str, object] | None = None, + cause: BaseException | None = None, + ) -> None: + super().__init__( + message="copy from git repo failed", + error_code=ErrorCode.GIT_COPY_ERROR, + op="materialize", + context={ + "src_root": src_root, + "dest": str(dest), + "stderr": stderr, + **_as_context(context), + }, + cause=cause, + ) + + +class MountArtifactError(ArtifactError): + """Base class for mount-related errors while materializing artifacts.""" + + +class MountToolMissingError(MountArtifactError): + """Required mount tool is missing in the sandbox.""" + + def __init__( + self, + *, + tool: str, + context: Mapping[str, object] | None = None, + cause: BaseException | None = None, + ) -> None: + super().__init__( + message=f"required mount tool missing: {tool}", + error_code=ErrorCode.MOUNT_MISSING_TOOL, + op="materialize", + context={"tool": tool, **_as_context(context)}, + cause=cause, + ) + + +class MountConfigError(MountArtifactError): + """Mount configuration was invalid or incomplete.""" + + def __init__( + self, + *, + message: str, + context: Mapping[str, object] | None = None, + ) -> None: + super().__init__( + message=message, + error_code=ErrorCode.MOUNT_CONFIG_INVALID, + op="materialize", + context=_as_context(context), + ) + + +class MountCommandError(MountArtifactError): + """Mount command failed to execute successfully.""" + + def __init__( + self, + *, + command: str, + stderr: str | None, + context: Mapping[str, object] | None = None, + cause: BaseException | None = None, + ) -> None: + super().__init__( + message="mount command failed", + error_code=ErrorCode.MOUNT_FAILED, + op="materialize", + context={"command": command, "stderr": stderr, **_as_context(context)}, + cause=cause, + ) + + +class SnapshotPersistError(SnapshotError): + """Failed to persist snapshot bytes to durable storage.""" + + def __init__( + self, + *, + snapshot_id: str, + path: Path, + context: Mapping[str, object] | None = None, + cause: BaseException | None = None, + ) -> None: + super().__init__( + message="failed to persist snapshot", + error_code=ErrorCode.SNAPSHOT_PERSIST_ERROR, + op="snapshot_persist", + context={"snapshot_id": snapshot_id, "path": str(path), **_as_context(context)}, + cause=cause, + ) + + +class SnapshotRestoreError(SnapshotError): + """Failed to restore snapshot bytes from durable storage.""" + + def __init__( + self, + *, + snapshot_id: str, + path: Path, + context: Mapping[str, object] | None = None, + cause: BaseException | None = None, + ) -> None: + super().__init__( + message="failed to restore snapshot", + error_code=ErrorCode.SNAPSHOT_RESTORE_ERROR, + op="snapshot_restore", + context={"snapshot_id": snapshot_id, "path": str(path), **_as_context(context)}, + cause=cause, + ) + + +class SnapshotNotRestorableError(SnapshotError): + """Snapshot cannot be restored because the underlying storage is missing.""" + + def __init__( + self, + *, + snapshot_id: str, + path: Path, + context: Mapping[str, object] | None = None, + ) -> None: + super().__init__( + message="snapshot is not restorable", + error_code=ErrorCode.SNAPSHOT_NOT_RESTORABLE, + op="snapshot_restore", + context={"snapshot_id": snapshot_id, "path": str(path), **_as_context(context)}, + ) diff --git a/src/agents/sandbox/files.py b/src/agents/sandbox/files.py new file mode 100644 index 0000000000..a49b1d346e --- /dev/null +++ b/src/agents/sandbox/files.py @@ -0,0 +1,26 @@ +from __future__ import annotations + +from enum import Enum + +from pydantic import BaseModel + +from .types import Permissions + + +class EntryKind(str, Enum): + DIRECTORY = "directory" + FILE = "file" + SYMLINK = "symlink" + OTHER = "other" + + +class FileEntry(BaseModel): + path: str + permissions: Permissions + owner: str + group: str + size: int + kind: EntryKind = EntryKind.FILE + + def is_dir(self) -> bool: + return self.kind == EntryKind.DIRECTORY diff --git a/src/agents/sandbox/manifest.py b/src/agents/sandbox/manifest.py new file mode 100644 index 0000000000..09b735771a --- /dev/null +++ b/src/agents/sandbox/manifest.py @@ -0,0 +1,142 @@ +import abc +import asyncio +from collections.abc import Iterator, Mapping +from pathlib import Path +from typing import Literal + +from pydantic import BaseModel, Field, field_serializer, field_validator +from typing_extensions import assert_never + +from .entries import BaseEntry, Dir +from .errors import InvalidManifestPathError +from .manifest_render import render_manifest_description +from .types import Group, User + + +# TODO (sdcoffey) env val from secret store +class EnvValue(BaseModel, abc.ABC): + @abc.abstractmethod + async def resolve(self) -> str: ... + + +class StrEnvValue(EnvValue): + value: str + + async def resolve(self) -> str: + return self.value + + +class EnvEntry(BaseModel): + description: str | None = None + ephemeral: bool = Field(default=False) + value: EnvValue + + +class Environment(BaseModel): + value: dict[str, str | EnvValue | EnvEntry] = Field(default_factory=dict) + + def normalized(self) -> dict[str, EnvEntry]: + result: dict[str, EnvEntry] = {} + for key, value in self.value.items(): + match value: + case str(): + result[key] = EnvEntry(value=StrEnvValue(value=value)) + case EnvValue(): + result[key] = EnvEntry(value=value) + case EnvEntry(): + result[key] = value + case _: + assert_never(value) + + return result + + async def resolve(self) -> dict[str, str]: + normalized = self.normalized() + keys = normalized.keys() + values = await asyncio.gather(*[normalized[key].value.resolve() for key in keys]) + return dict(zip(keys, values)) + + +class Manifest(BaseModel): + version: Literal[1] = 1 + root: str = Field(default="/workspace") + entries: dict[str | Path, BaseEntry] = Field(default_factory=dict) + environment: Environment = Field(default_factory=Environment) + users: list[User] = Field(default_factory=list) + groups: list[Group] = Field(default_factory=list) + + @field_validator("entries", mode="before") + @classmethod + def _parse_entries(cls, value: object) -> dict[str | Path, BaseEntry]: + if value is None: + return {} + if not isinstance(value, Mapping): + raise TypeError(f"Artifact mapping must be a mapping, got {type(value).__name__}") + return {key: BaseEntry.parse(entry) for key, entry in value.items()} + + @field_serializer("entries", when_used="json") + def _serialize_entries(self, entries: Mapping[str | Path, BaseEntry]) -> dict[str, object]: + out: dict[str, object] = {} + for key, entry in entries.items(): + key_str = key.as_posix() if isinstance(key, Path) else str(key) + out[key_str] = entry.model_dump(mode="json") + return out + + def validated_entries(self) -> dict[str | Path, BaseEntry]: + validated: dict[str | Path, BaseEntry] = dict(self.entries) + for _path, _artifact in self.iter_entries(): + pass + return validated + + def ephemeral_entry_paths(self, depth: int | None = 1) -> set[Path]: + _ = depth + return {path for path, artifact in self.iter_entries() if artifact.ephemeral} + + @staticmethod + def _coerce_rel_path(path: str | Path) -> Path: + return path if isinstance(path, Path) else Path(path) + + @staticmethod + def _validate_rel_path(rel: Path) -> None: + if rel.is_absolute(): + raise InvalidManifestPathError(rel=rel, reason="absolute") + if ".." in rel.parts: + raise InvalidManifestPathError(rel=rel, reason="escape_root") + + def iter_entries(self) -> Iterator[tuple[Path, BaseEntry]]: + stack = [ + (self._coerce_rel_path(path), artifact) + for path, artifact in reversed(list(self.entries.items())) + ] + while stack: + rel_path, artifact = stack.pop() + self._validate_rel_path(rel_path) + yield rel_path, artifact + if not isinstance(artifact, Dir): + continue + + for child_name, child_artifact in reversed(list(artifact.children.items())): + child_rel_path = rel_path / self._coerce_rel_path(child_name) + stack.append((child_rel_path, child_artifact)) + + def describe(self, depth: int | None = 1) -> str: + """ + print a nice fs representation of things inside root with inline descriptions + depth controls how deep the tree is rendered; None renders all levels + eg: + + /workspace (root) + ├── repo/ # /workspace/repo — my repo + │ └── README.md # /workspace/repo/README.md + ├── data/ # /workspace/data + │ └── config.json # /workspace/data/config.json — config + ├── mount-data/ # /workspace/mount-data (mount) + └── notes.txt # /workspace/notes.txt + ... + """ + return render_manifest_description( + root=self.root, + entries=self.validated_entries(), + coerce_rel_path=self._coerce_rel_path, + depth=depth, + ) diff --git a/src/agents/sandbox/manifest_render.py b/src/agents/sandbox/manifest_render.py new file mode 100644 index 0000000000..d33ce320b1 --- /dev/null +++ b/src/agents/sandbox/manifest_render.py @@ -0,0 +1,163 @@ +from __future__ import annotations + +from pathlib import Path +from typing import Callable + +from .entries import BaseEntry, Dir, Mount + + +def render_manifest_description( + *, + root: str, + entries: dict[str | Path, BaseEntry], + coerce_rel_path: Callable[[str | Path], Path], + depth: int | None = 1, +) -> str: + if depth is not None and depth <= 0: + raise ValueError("depth must be a non-zero positive integer or None") + + root = root.rstrip("/") or "/" + root_path = Path(root) + + def _mount_full_path(entry: str | Path, artifact: Mount) -> Path: + if artifact.mount_path is not None: + mount_path = Path(artifact.mount_path) + return mount_path if mount_path.is_absolute() else root_path / mount_path + return root_path / coerce_rel_path(entry) + + class _Node: + def __init__(self) -> None: + self.children: dict[str, _Node] = {} + self.description: str | None = None + self.is_dir: bool = False + self.full_path: Path | None = None + + def _path_parts(path: Path) -> tuple[str, ...]: + parts = [part for part in path.parts if part not in {"", "."}] + return tuple(parts) + + root_node = _Node() + + def _insert_path( + path: Path, + *, + description: str | None, + is_dir: bool, + full_path: Path | None = None, + max_depth: int | None = None, + ) -> None: + parts = _path_parts(path) + if not parts: + return + node = root_node + limit = len(parts) if max_depth is None else min(len(parts), max_depth) + for index, part in enumerate(parts[:limit]): + node = node.children.setdefault(part, _Node()) + if index < len(parts) - 1: + node.is_dir = True + if node.description is None and description is not None and limit == len(parts): + node.description = description + if full_path is not None and limit == len(parts): + node.full_path = full_path + if is_dir or limit < len(parts): + node.is_dir = True + + def _insert_entry_tree( + path: Path, + artifact: BaseEntry, + *, + full_path: Path | None = None, + ) -> None: + stack: list[tuple[Path, BaseEntry, Path | None]] = [(path, artifact, full_path)] + while stack: + current_path, current_artifact, current_full_path = stack.pop() + _insert_path( + current_path, + description=current_artifact.description, + is_dir=current_artifact.permissions.directory, + full_path=current_full_path, + max_depth=depth, + ) + if not isinstance(current_artifact, Dir): + continue + if depth is not None and len(_path_parts(current_path)) >= depth: + continue + + for child_name, child_artifact in current_artifact.children.items(): + child_rel_path = coerce_rel_path(child_name) + child_path = current_path / child_rel_path + child_full_path = ( + current_full_path / child_rel_path if current_full_path is not None else None + ) + stack.append((child_path, child_artifact, child_full_path)) + + for entry, artifact in entries.items(): + path = coerce_rel_path(entry) + if path.is_absolute(): + path = path.relative_to(path.anchor) + full_path = _mount_full_path(entry, artifact) if isinstance(artifact, Mount) else None + _insert_entry_tree(path, artifact, full_path=full_path) + + def _collect( + node: _Node, + prefix: str, + remaining: int | None, + rel_parts: tuple[str, ...], + ) -> list[tuple[str, str, str, str | None]]: + lines: list[tuple[str, str, str, str | None]] = [] + stack: list[tuple[str, _Node, str, int | None, tuple[str, ...]]] + stack = [("children", node, prefix, remaining, rel_parts)] + while stack: + action, current_node, current_prefix, current_remaining, current_rel_parts = stack.pop() + if action == "line": + child = current_node + name = current_rel_parts[-1] + child_is_dir = child.is_dir or bool(child.children) + display_name = f"{name}/" if child_is_dir else name + if child.full_path is not None: + full_path = str(child.full_path) + else: + full_path = str(root_path / Path(*current_rel_parts)) + lines.append((current_prefix, display_name, full_path, child.description)) + continue + + if current_remaining is not None and current_remaining <= 0: + continue + + names = sorted(current_node.children) + next_remaining = None if current_remaining is None else current_remaining - 1 + for index in range(len(names) - 1, -1, -1): + name = names[index] + child = current_node.children[name] + is_last = index == len(names) - 1 + connector = "└── " if is_last else "├── " + child_parts = current_rel_parts + (name,) + if next_remaining is None or next_remaining > 0: + extension = " " if is_last else "│ " + stack.append( + ( + "children", + child, + current_prefix + extension, + next_remaining, + child_parts, + ) + ) + stack.append( + ("line", child, current_prefix + connector, next_remaining, child_parts) + ) + return lines + + lines: list[str] = [root] + collected = _collect(root_node, "", depth, ()) + if collected: + max_width = max(len(prefix + name) for prefix, name, _, _ in collected) + for prefix, name, full_path_str, description in collected: + spacer = " " * (max_width - len(prefix + name) + 2) + if description: + comment = f"# {full_path_str} — {description}" + else: + comment = f"# {full_path_str}" + lines.append(f"{prefix}{name}{spacer}{comment}") + + return "\n".join(lines) + "\n" diff --git a/src/agents/sandbox/materialization.py b/src/agents/sandbox/materialization.py new file mode 100644 index 0000000000..25528db690 --- /dev/null +++ b/src/agents/sandbox/materialization.py @@ -0,0 +1,68 @@ +import asyncio +from collections.abc import Awaitable, Callable, Sequence +from dataclasses import dataclass +from pathlib import Path +from typing import TypeVar, cast + + +@dataclass(frozen=True) +class MaterializedFile: + path: Path + sha256: str + + +@dataclass(frozen=True) +class MaterializationResult: + files: list[MaterializedFile] + + +_TaskResultT = TypeVar("_TaskResultT") +_MISSING = object() + + +async def gather_in_order( + task_factories: Sequence[Callable[[], Awaitable[_TaskResultT]]], +) -> list[_TaskResultT]: + if not task_factories: + return [] + + results: list[_TaskResultT | object] = [_MISSING] * len(task_factories) + + async def _run(index: int, factory: Callable[[], Awaitable[_TaskResultT]]) -> None: + results[index] = await factory() + + tasks = [ + asyncio.create_task(_run(index, factory)) for index, factory in enumerate(task_factories) + ] + try: + done, pending = await asyncio.wait(tasks, return_when=asyncio.FIRST_EXCEPTION) + + first_error: BaseException | None = None + for task in done: + try: + task.result() + except asyncio.CancelledError: + continue + except BaseException as error: + first_error = error + break + + if first_error is not None: + for task in pending: + task.cancel() + await asyncio.gather(*pending, return_exceptions=True) + raise first_error + + if pending: + await asyncio.gather(*pending) + except BaseException: + for task in tasks: + if not task.done(): + task.cancel() + await asyncio.gather(*tasks, return_exceptions=True) + raise + + for task in tasks: + task.result() + + return [cast(_TaskResultT, result) for result in results] diff --git a/src/agents/sandbox/py.typed b/src/agents/sandbox/py.typed new file mode 100644 index 0000000000..e69de29bb2 diff --git a/src/agents/sandbox/runtime.py b/src/agents/sandbox/runtime.py new file mode 100644 index 0000000000..4a44bec4e8 --- /dev/null +++ b/src/agents/sandbox/runtime.py @@ -0,0 +1,125 @@ +from __future__ import annotations + +from dataclasses import dataclass +from typing import Generic + +from ..agent import Agent +from ..exceptions import UserError +from ..items import TResponseInputItem +from ..result import RunResult, RunResultStreaming +from ..run_config import RunConfig +from ..run_context import RunContextWrapper, TContext +from ..run_internal.agent_bindings import ( + AgentBindings, + bind_execution_agent, + bind_public_agent, +) +from ..run_state import RunState +from .runtime_agent_preparation import clone_capabilities, prepare_sandbox_agent +from .runtime_session_manager import SandboxRuntimeSessionManager +from .sandbox_agent import SandboxAgent +from .session.base_sandbox_session import BaseSandboxSession + + +@dataclass +class _SandboxPreparedAgent(Generic[TContext]): + bindings: AgentBindings[TContext] + input: str | list[TResponseInputItem] + + +class SandboxRuntime(Generic[TContext]): + def __init__( + self, + *, + starting_agent: Agent[TContext], + run_config: RunConfig | None, + run_state: RunState[TContext] | None, + ) -> None: + self._sandbox_config = run_config.sandbox if run_config is not None else None + self._session_manager = SandboxRuntimeSessionManager( + starting_agent=starting_agent, + sandbox_config=self._sandbox_config, + run_state=run_state, + ) + self._prepared_agents: dict[int, Agent[TContext]] = {} + self._prepared_sessions: dict[int, BaseSandboxSession] = {} + + @property + def enabled(self) -> bool: + return self._session_manager.enabled + + @property + def current_session(self) -> BaseSandboxSession | None: + return self._session_manager.current_session + + def apply_result_metadata(self, result: RunResult | RunResultStreaming) -> None: + session = self.current_session + result._sandbox_session = session + if isinstance(result, RunResultStreaming): + + async def _cleanup_and_store() -> None: + try: + payload = await self.cleanup() + result._sandbox_resume_state = payload + finally: + result._sandbox_session = None + + result._sandbox_cleanup = _cleanup_and_store + + def assert_agent_supported(self, agent: Agent[TContext]) -> None: + if isinstance(agent, SandboxAgent) and self._sandbox_config is None: + raise UserError("SandboxAgent execution requires `RunConfig(sandbox=...)`") + + async def prepare_agent( + self, + *, + current_agent: Agent[TContext], + current_input: str | list[TResponseInputItem], + context_wrapper: RunContextWrapper[TContext], + is_resumed_state: bool, + ) -> _SandboxPreparedAgent[TContext]: + self.assert_agent_supported(current_agent) + if not isinstance(current_agent, SandboxAgent): + return _SandboxPreparedAgent( + bindings=bind_public_agent(current_agent), + input=current_input, + ) + + self._session_manager.acquire_agent(current_agent) + prepared_agent = self._prepared_agents.get(id(current_agent)) + prepared_capabilities = clone_capabilities(current_agent.capabilities) + session = await self._session_manager.ensure_session( + agent=current_agent, + capabilities=prepared_capabilities, + is_resumed_state=is_resumed_state, + ) + if prepared_agent is not None and self._prepared_sessions.get(id(current_agent)) is session: + return _SandboxPreparedAgent( + bindings=bind_execution_agent( + public_agent=current_agent, + execution_agent=prepared_agent, + ), + input=current_input, + ) + + prepared_agent = prepare_sandbox_agent( + agent=current_agent, + session=session, + capabilities=prepared_capabilities, + ) + self._prepared_agents[id(current_agent)] = prepared_agent + self._prepared_sessions[id(current_agent)] = session + return _SandboxPreparedAgent( + bindings=bind_execution_agent( + public_agent=current_agent, + execution_agent=prepared_agent, + ), + input=current_input, + ) + + async def cleanup(self) -> dict[str, object] | None: + try: + return await self._session_manager.cleanup() + finally: + self._prepared_agents.clear() + self._prepared_sessions.clear() diff --git a/src/agents/sandbox/runtime_agent_preparation.py b/src/agents/sandbox/runtime_agent_preparation.py new file mode 100644 index 0000000000..281f792c78 --- /dev/null +++ b/src/agents/sandbox/runtime_agent_preparation.py @@ -0,0 +1,97 @@ +from __future__ import annotations + +import inspect +from collections.abc import Awaitable, Callable +from typing import cast + +from .._public_agent import get_public_agent, set_public_agent +from ..agent import Agent +from ..run_context import RunContextWrapper, TContext +from .capabilities import Capability +from .manifest import Manifest +from .sandbox_agent import SandboxAgent +from .session.base_sandbox_session import BaseSandboxSession + + +def clone_capabilities(capabilities: list[Capability]) -> list[Capability]: + return [capability.clone() for capability in capabilities] + + +def prepare_sandbox_agent( + *, + agent: SandboxAgent[TContext], + session: BaseSandboxSession, + capabilities: list[Capability], +) -> Agent[TContext]: + manifest = session.state.manifest + for capability in capabilities: + capability.bind(session) + + capability_tools = [tool for capability in capabilities for tool in capability.tools()] + prepared_agent = agent.clone( + instructions=build_sandbox_instructions( + agent.instructions, + agent.developer_instructions, + capabilities, + manifest, + ), + tools=[*agent.tools, *capability_tools], + capabilities=capabilities, + ) + set_public_agent(prepared_agent, agent) + return prepared_agent + + +def build_sandbox_instructions( + base_instructions: str + | Callable[[RunContextWrapper[TContext], Agent[TContext]], Awaitable[str | None] | str | None] + | None, + developer_instructions: str | None, + capabilities: list[Capability], + manifest: Manifest | None, +) -> Callable[[RunContextWrapper[TContext], Agent[TContext]], Awaitable[str | None]]: + async def _instructions( + run_context: RunContextWrapper[TContext], + current_agent: Agent[TContext], + ) -> str | None: + parts: list[str] = [] + public_agent = cast(Agent[TContext], get_public_agent(current_agent)) + + base = await resolve_base_instructions( + instructions=base_instructions, + run_context=run_context, + agent=public_agent, + ) + if base: + parts.append(base) + + if developer_instructions: + parts.append(developer_instructions) + + if manifest is not None: + for capability in capabilities: + fragment = await capability.instructions(manifest) + if fragment: + parts.append(fragment) + + return "\n\n".join(parts) if parts else None + + return _instructions + + +async def resolve_base_instructions( + *, + instructions: str + | Callable[[RunContextWrapper[TContext], Agent[TContext]], Awaitable[str | None] | str | None] + | None, + run_context: RunContextWrapper[TContext], + agent: Agent[TContext], +) -> str | None: + if isinstance(instructions, str): + return instructions + if callable(instructions): + result = instructions(run_context, agent) + if inspect.isawaitable(result): + return await result + return result + return None diff --git a/src/agents/sandbox/runtime_session_manager.py b/src/agents/sandbox/runtime_session_manager.py new file mode 100644 index 0000000000..0ee112e8dd --- /dev/null +++ b/src/agents/sandbox/runtime_session_manager.py @@ -0,0 +1,631 @@ +from __future__ import annotations + +import asyncio +import copy +import threading +from dataclasses import dataclass, field +from typing import Any, Generic, cast + +from ..agent import Agent +from ..run_config import SandboxRunConfig +from ..run_context import TContext +from ..run_state import ( + RunState, + _allocate_unique_agent_identity, + _build_agent_identity_keys_by_id, +) +from .capabilities import Capability +from .manifest import Manifest +from .sandbox_agent import SandboxAgent +from .session.base_sandbox_session import BaseSandboxSession +from .session.sandbox_client import BaseSandboxClient +from .session.sandbox_session import SandboxSession +from .session.sandbox_session_state import SandboxSessionState +from .snapshot import NoopSnapshotSpec, SnapshotSpec +from .snapshot_defaults import resolve_default_local_snapshot_spec + + +class _SandboxSessionResources: + def __init__( + self, + *, + session: BaseSandboxSession, + client: BaseSandboxClient[Any] | None, + owns_session: bool, + ) -> None: + self._session = session + self._client = client + self._owns_session = owns_session + self._cleanup_lock = asyncio.Lock() + self._cleaned = False + self._started = False + + @property + def session(self) -> BaseSandboxSession: + return self._session + + @property + def state(self) -> SandboxSessionState: + return self._session.state + + async def ensure_started(self) -> None: + if self._started and await self._session.running(): + return + if not self._owns_session and await self._session.running(): + self._started = True + return + await self._session.start() + self._started = True + + async def cleanup(self) -> None: + if not self._owns_session: + return + async with self._cleanup_lock: + if self._cleaned: + return + self._cleaned = True + + cleanup_error: BaseException | None = None + try: + await self._session.stop() + except BaseException as exc: # pragma: no cover + cleanup_error = exc + try: + await self._session.shutdown() + except BaseException as exc: # pragma: no cover + if cleanup_error is None: + cleanup_error = exc + finally: + try: + if self._client is not None and isinstance(self._session, SandboxSession): + await self._client.delete(self._session) + except BaseException as exc: # pragma: no cover + if cleanup_error is None: + cleanup_error = exc + finally: + try: + await self._session._aclose_dependencies() + except BaseException as exc: # pragma: no cover + if cleanup_error is None: + cleanup_error = exc + if cleanup_error is not None: + raise cleanup_error + + +@dataclass +class _SandboxConcurrencyGuard: + lock: threading.Lock = field(default_factory=threading.Lock) + active_runs: int = 0 + + +class SandboxRuntimeSessionManager(Generic[TContext]): + def __init__( + self, + *, + starting_agent: Agent[TContext], + sandbox_config: SandboxRunConfig | None, + run_state: RunState[TContext] | None, + ) -> None: + self._sandbox_config = sandbox_config + self._run_state = run_state + resume_identity_root = starting_agent + if ( + run_state is not None + and run_state._starting_agent is not None + and run_state._current_agent is not None + and run_state._starting_agent is not run_state._current_agent + ): + resume_identity_root = run_state._starting_agent + self._stable_resume_keys_by_agent_id = _build_agent_identity_keys_by_id( + resume_identity_root + ) + self._resources_by_agent: dict[int, _SandboxSessionResources] = {} + self._current_agent_id: int | None = None + self._acquired_agents: dict[int, SandboxAgent[TContext]] = {} + self._resume_keys_by_agent_id: dict[int, str] = {} + self._resume_source_key_by_agent_id: dict[int, str] = {} + self._available_resumed_keys_by_name: dict[str, list[str]] | None = None + self._claimed_resumed_keys: set[str] = set() + + @staticmethod + def _resume_agent_base_key(agent: Agent[Any]) -> str: + return agent.name + + @staticmethod + def _serialize_session_entry( + *, + agent: Agent[Any], + session_state: dict[str, object], + ) -> dict[str, object]: + return { + "agent_name": agent.name, + "session_state": session_state, + } + + @property + def enabled(self) -> bool: + return self._sandbox_config is not None + + @property + def current_session(self) -> BaseSandboxSession | None: + if self._current_agent_id is None: + return None + resources = self._resources_by_agent.get(self._current_agent_id) + if resources is None: + return None + return resources.session + + def acquire_agent(self, agent: SandboxAgent[TContext]) -> None: + agent_id = id(agent) + if agent_id in self._acquired_agents: + return + + guard = getattr(agent, "_sandbox_concurrency_guard", None) + if guard is None: + guard = _SandboxConcurrencyGuard() + agent._sandbox_concurrency_guard = guard + with guard.lock: + if guard.active_runs > 0: + raise RuntimeError( + f"SandboxAgent {agent.name!r} cannot be reused concurrently across runs" + ) + guard.active_runs += 1 + self._acquired_agents[agent_id] = agent + self._ensure_resume_key(agent) + + async def ensure_session( + self, + *, + agent: SandboxAgent[TContext], + capabilities: list[Capability], + is_resumed_state: bool, + ) -> BaseSandboxSession: + agent_id = id(agent) + resources = self._resources_by_agent.get(agent_id) + if resources is None: + resources = await self._create_resources( + agent=agent, + capabilities=capabilities, + is_resumed_state=is_resumed_state, + ) + self._resources_by_agent[agent_id] = resources + self._current_agent_id = agent_id + + await resources.ensure_started() + return resources.session + + def serialize_resume_state(self) -> dict[str, object] | None: + existing_payload = ( + copy.deepcopy(self._run_state._sandbox) + if self._run_state is not None and isinstance(self._run_state._sandbox, dict) + else None + ) + if self._sandbox_config is None: + return existing_payload + if self._current_agent_id is None: + return existing_payload + if self._sandbox_config.client is None: + return existing_payload + resources = self._resources_by_agent.get(self._current_agent_id) + if resources is None: + return existing_payload + + client = self._resolve_client() + current_agent = self._acquired_agents.get(self._current_agent_id) + if current_agent is None: + return existing_payload + + sessions_by_agent = self._serialize_sessions_by_agent(client) + return { + "backend_id": client.backend_id, + "current_agent_key": self._ensure_resume_key(current_agent), + "current_agent_name": current_agent.name, + "session_state": client.serialize_session_state(resources.state), + "sessions_by_agent": sessions_by_agent, + } + + async def cleanup(self) -> dict[str, object] | None: + cleanup_error: BaseException | None = None + resume_state: dict[str, object] | None = None + try: + for resources in list(self._resources_by_agent.values()): + try: + await resources.cleanup() + except BaseException as exc: # pragma: no cover + if cleanup_error is None: + cleanup_error = exc + if cleanup_error is None: + resume_state = self.serialize_resume_state() + finally: + self._resources_by_agent.clear() + self._current_agent_id = None + self._release_agents() + if cleanup_error is not None: + raise cleanup_error + return resume_state + + async def _create_resources( + self, + *, + agent: SandboxAgent[TContext], + capabilities: list[Capability], + is_resumed_state: bool, + ) -> _SandboxSessionResources: + sandbox_config = self._require_sandbox_config() + if sandbox_config.session is not None: + return _SandboxSessionResources( + session=sandbox_config.session, + client=None, + owns_session=False, + ) + + client = self._resolve_client() + explicit_state = sandbox_config.session_state + resume_from_run_state = False + resumed_payload = self._resume_state_payload_for_agent( + client=client, + agent=agent, + agent_id=id(agent), + ) + if resumed_payload is not None: + explicit_state = client.deserialize_session_state(resumed_payload) + resume_from_run_state = True + + if explicit_state is not None: + explicit_state = self._process_resumed_state_manifest( + capabilities=capabilities, + session_state=explicit_state, + ) + return _SandboxSessionResources( + session=await client.resume(explicit_state), + client=client, + owns_session=True, + ) + + effective_manifest = self._resolve_manifest( + agent=agent, + resume_from_run_state=resume_from_run_state, + ) + if effective_manifest is not None: + effective_manifest = self._process_manifest(capabilities, effective_manifest) + + options = sandbox_config.options + if options is None and not client.supports_default_options: + raise ValueError( + "Sandbox execution requires `run_config.sandbox.options` when creating a session" + ) + + session = await client.create( + snapshot=self._resolve_snapshot_spec(sandbox_config.snapshot), + manifest=effective_manifest, + options=options, + ) + return _SandboxSessionResources(session=session, client=client, owns_session=True) + + def _resume_state_payload_for_agent( + self, + *, + client: BaseSandboxClient[Any], + agent: SandboxAgent[TContext], + agent_id: int, + ) -> dict[str, object] | None: + if self._run_state is None or self._run_state._sandbox is None: + return None + + resumed = self._run_state._sandbox + backend_id = resumed.get("backend_id") + if backend_id != client.backend_id: + raise ValueError( + "RunState sandbox backend does not match the configured sandbox client" + ) + + sessions_by_agent = resumed.get("sessions_by_agent") + if isinstance(sessions_by_agent, dict): + resume_key = self._assign_resumed_agent_key(agent) + if resume_key is not None: + payload = self._session_payload_from_entry(sessions_by_agent.get(resume_key)) + if payload is not None: + self._remember_resume_source_key(agent_id, resume_key) + return payload + + payload = self._session_payload_from_entry(sessions_by_agent.get(str(agent_id))) + if payload is not None: + self._remember_resume_source_key(agent_id, str(agent_id)) + return payload + + current_agent_key = resumed.get("current_agent_key") + current_agent_name = resumed.get("current_agent_name") + current_agent_id = resumed.get("current_agent_id") + payload = resumed.get("session_state") + if payload is None: + return None + if not isinstance(payload, dict): + raise ValueError("RunState sandbox payload is missing `session_state`") + if isinstance(current_agent_key, str): + resume_key = self._assign_resumed_agent_key(agent) + if resume_key != current_agent_key: + return None + self._remember_resume_source_key(agent_id, current_agent_key) + return payload + if current_agent_name is None and self._run_state._current_agent is not None: + current_agent_name = self._run_state._current_agent.name + if isinstance(current_agent_name, str): + if current_agent_name != self._resume_agent_base_key(agent): + return None + self._remember_resume_source_key(agent_id, current_agent_name) + return payload + if current_agent_id is None or current_agent_id == agent_id: + if current_agent_id is not None: + self._remember_resume_source_key(agent_id, str(current_agent_id)) + return payload + return None + + def _resolve_client(self) -> BaseSandboxClient[Any]: + sandbox_config = self._require_sandbox_config() + if sandbox_config.client is None: + raise ValueError( + "Sandbox execution requires `run_config.sandbox.client` " + "unless a live session is provided" + ) + return sandbox_config.client + + def _require_sandbox_config(self) -> SandboxRunConfig: + if self._sandbox_config is None: + raise ValueError("Sandbox runtime is disabled for this run") + return self._sandbox_config + + @staticmethod + def _resolve_snapshot_spec(snapshot: SnapshotSpec | None) -> SnapshotSpec: + if snapshot is not None: + return snapshot + try: + return resolve_default_local_snapshot_spec() + except OSError: + return NoopSnapshotSpec() + + def _resolve_manifest( + self, + *, + agent: SandboxAgent[TContext], + resume_from_run_state: bool, + ) -> Manifest | None: + sandbox_config = self._require_sandbox_config() + if sandbox_config.session is not None: + return cast(Manifest | None, getattr(sandbox_config.session.state, "manifest", None)) + if sandbox_config.session_state is not None: + return cast(Manifest | None, getattr(sandbox_config.session_state, "manifest", None)) + if resume_from_run_state: + return None + if sandbox_config.manifest is not None: + return sandbox_config.manifest + return agent.default_manifest + + @staticmethod + def _process_manifest( + capabilities: list[Capability], + manifest: Manifest | None, + ) -> Manifest | None: + if manifest is None: + return None + processed_manifest = manifest.model_copy(deep=True) + for capability in capabilities: + processed_manifest = capability.process_manifest(processed_manifest) + return processed_manifest + + @classmethod + def _process_resumed_state_manifest( + cls, + *, + capabilities: list[Capability], + session_state: SandboxSessionState, + ) -> SandboxSessionState: + processed_manifest = cls._process_manifest(capabilities, session_state.manifest) + if processed_manifest is None: + return session_state + return session_state.model_copy(update={"manifest": processed_manifest}) + + def _release_agents(self) -> None: + if not self._acquired_agents: + return + + released = list(self._acquired_agents.values()) + self._acquired_agents.clear() + self._resume_keys_by_agent_id.clear() + self._resume_source_key_by_agent_id.clear() + self._available_resumed_keys_by_name = None + self._claimed_resumed_keys.clear() + for agent in released: + guard = getattr(agent, "_sandbox_concurrency_guard", None) + if guard is None: + continue + with guard.lock: + guard.active_runs = max(0, guard.active_runs - 1) + + def _ensure_resume_key(self, agent: SandboxAgent[TContext]) -> str: + agent_id = id(agent) + existing = self._resume_keys_by_agent_id.get(agent_id) + if existing is not None: + return existing + + stable_key = self._stable_resume_key_for_agent(agent) + if stable_key is not None and stable_key not in self._used_resume_keys(): + self._resume_keys_by_agent_id[agent_id] = stable_key + return stable_key + + resumed_key = self._assign_resumed_agent_key(agent) + if resumed_key is not None: + return resumed_key + + key = _allocate_unique_agent_identity( + self._resume_agent_base_key(agent), + self._used_resume_keys(), + ) + self._resume_keys_by_agent_id[agent_id] = key + return key + + def _stable_resume_key_for_agent(self, agent: Agent[Any]) -> str | None: + return self._stable_resume_keys_by_agent_id.get(id(agent)) + + def _assign_resumed_agent_key(self, agent: SandboxAgent[TContext]) -> str | None: + agent_id = id(agent) + existing = self._resume_keys_by_agent_id.get(agent_id) + if existing is not None: + return existing + if self._run_state is None or self._run_state._sandbox is None: + return None + + resumed = self._run_state._sandbox + current_key = resumed.get("current_agent_key") + stable_key = self._stable_resume_key_for_agent(agent) + sessions_by_agent = resumed.get("sessions_by_agent") + if ( + isinstance(stable_key, str) + and stable_key not in self._claimed_resumed_keys + and self._entry_matches_agent_name(sessions_by_agent, stable_key, agent.name) + ): + self._claimed_resumed_keys.add(stable_key) + self._resume_keys_by_agent_id[agent_id] = stable_key + return stable_key + + base = self._resume_agent_base_key(agent) + if ( + isinstance(current_key, str) + and current_key not in self._claimed_resumed_keys + and self._run_state._current_agent is agent + and self._entry_matches_agent_name( + sessions_by_agent, + current_key, + base, + ) + ): + self._claimed_resumed_keys.add(current_key) + self._resume_keys_by_agent_id[agent_id] = current_key + return current_key + + available = self._resumed_keys_by_name().get(base, []) + for key in available: + if key in self._claimed_resumed_keys: + continue + if ( + isinstance(current_key, str) + and key == current_key + and self._run_state._current_agent is not agent + ): + continue + self._claimed_resumed_keys.add(key) + self._resume_keys_by_agent_id[agent_id] = key + return key + return None + + def _resumed_keys_by_name(self) -> dict[str, list[str]]: + cached = self._available_resumed_keys_by_name + if cached is not None: + return cached + + grouped: dict[str, list[str]] = {} + if self._run_state is not None and self._run_state._sandbox is not None: + sessions_by_agent = self._run_state._sandbox.get("sessions_by_agent") + if isinstance(sessions_by_agent, dict): + for key, entry in sessions_by_agent.items(): + if not isinstance(key, str): + continue + agent_name = self._agent_name_from_entry(key=key, entry=entry) + if agent_name is None: + continue + grouped.setdefault(agent_name, []).append(key) + + self._available_resumed_keys_by_name = grouped + return grouped + + def _legacy_session_entries(self) -> dict[str, object]: + if self._run_state is None or self._run_state._sandbox is None: + return {} + + resumed = self._run_state._sandbox + sessions_by_agent = resumed.get("sessions_by_agent") + if isinstance(sessions_by_agent, dict): + return { + key: copy.deepcopy(entry) + for key, entry in sessions_by_agent.items() + if isinstance(key, str) + } + + payload = resumed.get("session_state") + if not isinstance(payload, dict): + return {} + + current_key = resumed.get("current_agent_key") + if isinstance(current_key, str): + return {current_key: copy.deepcopy(payload)} + + current_agent_name = resumed.get("current_agent_name") + if current_agent_name is None and self._run_state._current_agent is not None: + current_agent_name = self._run_state._current_agent.name + if isinstance(current_agent_name, str): + return {current_agent_name: copy.deepcopy(payload)} + + current_agent_id = resumed.get("current_agent_id") + if current_agent_id is not None: + return {str(current_agent_id): copy.deepcopy(payload)} + return {} + + def _serialize_sessions_by_agent( + self, + client: BaseSandboxClient[Any], + ) -> dict[str, object]: + sessions_by_agent = self._legacy_session_entries() + for agent_id, agent_resources in self._resources_by_agent.items(): + agent = self._acquired_agents.get(agent_id) + if agent is None: + continue + resume_key = self._ensure_resume_key(agent) + source_key = self._resume_source_key_by_agent_id.get(agent_id) + if source_key is not None and source_key != resume_key: + sessions_by_agent.pop(source_key, None) + sessions_by_agent[resume_key] = self._serialize_session_entry( + agent=agent, + session_state=client.serialize_session_state(agent_resources.state), + ) + return sessions_by_agent + + def _used_resume_keys(self) -> set[str]: + used = set(self._legacy_session_entries()) + used.update(self._resume_keys_by_agent_id.values()) + return used + + def _remember_resume_source_key(self, agent_id: int, key: str) -> None: + self._resume_source_key_by_agent_id[agent_id] = key + + @staticmethod + def _entry_matches_agent_name( + sessions_by_agent: object, + key: str, + agent_name: str, + ) -> bool: + if not isinstance(sessions_by_agent, dict): + return False + entry = sessions_by_agent.get(key) + return ( + SandboxRuntimeSessionManager._agent_name_from_entry(key=key, entry=entry) == agent_name + ) + + @staticmethod + def _agent_name_from_entry(*, key: str, entry: object) -> str | None: + if isinstance(entry, dict): + entry_name = entry.get("agent_name") + session_state = entry.get("session_state") + if isinstance(entry_name, str) and isinstance(session_state, dict): + return entry_name + return key + return None + + @staticmethod + def _session_payload_from_entry(entry: object) -> dict[str, object] | None: + if entry is None: + return None + if not isinstance(entry, dict): + raise ValueError("RunState sandbox payload has an invalid `sessions_by_agent` item") + session_state = entry.get("session_state") + if isinstance(session_state, dict): + return session_state + return entry diff --git a/src/agents/sandbox/sandbox_agent.py b/src/agents/sandbox/sandbox_agent.py new file mode 100644 index 0000000000..122c5b7568 --- /dev/null +++ b/src/agents/sandbox/sandbox_agent.py @@ -0,0 +1,28 @@ +from __future__ import annotations + +from dataclasses import dataclass, field + +from ..agent import Agent +from ..run_context import TContext +from .capabilities import Capability +from .manifest import Manifest + + +@dataclass +class SandboxAgent(Agent[TContext]): + """An `Agent` with sandbox-specific configuration. + + Runtime transport details such as the sandbox client, client options, and live session are + provided at run time through `RunConfig(sandbox=...)`, not stored on the agent itself. + """ + + default_manifest: Manifest | None = None + """Default sandbox manifest for new sessions created by `Runner` sandbox execution.""" + + developer_instructions: str | None = None + """Additional deterministic instructions appended after the base agent instructions.""" + + capabilities: list[Capability] = field(default_factory=list) + """Sandbox capabilities that can mutate the manifest, add instructions, and expose tools.""" + + _sandbox_concurrency_guard: object | None = field(default=None, init=False, repr=False) diff --git a/src/agents/sandbox/sandboxes/__init__.py b/src/agents/sandbox/sandboxes/__init__.py new file mode 100644 index 0000000000..fa4d02611b --- /dev/null +++ b/src/agents/sandbox/sandboxes/__init__.py @@ -0,0 +1,41 @@ +""" +Sandbox implementations for the sandbox package. + +This subpackage contains concrete session/client implementations for different +execution environments (e.g. Docker, local Unix). +""" + +from .unix_local import ( + UnixLocalSandboxClient, + UnixLocalSandboxSession, + UnixLocalSandboxSessionState, +) + +try: + from .docker import ( # noqa: F401 + DockerSandboxClient, + DockerSandboxClientOptions, + DockerSandboxSession, + DockerSandboxSessionState, + ) + + _HAS_DOCKER = True +except Exception: # pragma: no cover + # Docker is an optional extra; keep base imports working without it. + _HAS_DOCKER = False + +__all__ = [ + "UnixLocalSandboxClient", + "UnixLocalSandboxSession", + "UnixLocalSandboxSessionState", +] + +if _HAS_DOCKER: + __all__.extend( + [ + "DockerSandboxClient", + "DockerSandboxClientOptions", + "DockerSandboxSession", + "DockerSandboxSessionState", + ] + ) diff --git a/src/agents/sandbox/sandboxes/docker.py b/src/agents/sandbox/sandboxes/docker.py new file mode 100644 index 0000000000..f292b1f00b --- /dev/null +++ b/src/agents/sandbox/sandboxes/docker.py @@ -0,0 +1,619 @@ +import asyncio +import io +import tarfile +import tempfile +import uuid +from concurrent.futures import ThreadPoolExecutor +from dataclasses import dataclass +from pathlib import Path +from typing import Final, cast + +import docker.errors # type: ignore[import-untyped] +from docker import DockerClient as DockerSDKClient +from docker.models.containers import Container # type: ignore[import-untyped] +from docker.utils import parse_repository_tag # type: ignore[import-untyped] + +from ..entries import ( + FuseMountPattern, + Mount, + MountpointMountPattern, + RcloneMountPattern, + resolve_workspace_path, +) +from ..errors import ( + ExecTimeoutError, + ExecTransportError, + WorkspaceArchiveReadError, + WorkspaceArchiveWriteError, + WorkspaceReadNotFoundError, +) +from ..manifest import Manifest +from ..session import SandboxSession, SandboxSessionState +from ..session.base_sandbox_session import BaseSandboxSession +from ..session.dependencies import Dependencies +from ..session.manager import Instrumentation +from ..session.sandbox_client import BaseSandboxClient +from ..session.workspace_payloads import coerce_write_payload +from ..snapshot import SnapshotSpec, resolve_snapshot +from ..types import ExecResult +from ..util.iterator_io import IteratorIO +from ..util.retry import ( + TRANSIENT_HTTP_STATUS_CODES, + exception_chain_has_status_code, + retry_async, +) +from ..util.tar_utils import should_skip_tar_member + +_DOCKER_EXECUTOR: Final = ThreadPoolExecutor( + max_workers=8, + thread_name_prefix="agents-docker-sandbox", +) + + +class DockerSandboxSessionState(SandboxSessionState): + image: str + container_id: str + workspace_root_ready: bool = False + + +@dataclass(frozen=True) +class DockerSandboxClientOptions: + image: str + + +class DockerSandboxSession(BaseSandboxSession): + _docker_client: DockerSDKClient + _container: Container + _workspace_root_ready: bool + _resume_workspace_probe_pending: bool + _resume_preserves_system_state: bool + + state: DockerSandboxSessionState + _ARCHIVE_STAGING_DIR: Path = Path("/tmp/uc-docker-archive") + + def __init__( + self, + *, + docker_client: DockerSDKClient, + container: Container, + state: DockerSandboxSessionState, + ) -> None: + self._docker_client = docker_client + self._container = container + self.state = state + self._workspace_root_ready = state.workspace_root_ready + self._resume_workspace_probe_pending = False + self._resume_preserves_system_state = False + + @classmethod + def from_state( + cls, + state: DockerSandboxSessionState, + *, + container: Container, + docker_client: DockerSDKClient, + ) -> "DockerSandboxSession": + return cls(docker_client=docker_client, container=container, state=state) + + @property + def container_id(self) -> str: + return self.state.container_id + + def _archive_stage_path(self, *, name_hint: str) -> Path: + # Unique name avoids clashes across concurrent reads/writes. + return self._ARCHIVE_STAGING_DIR / f"{uuid.uuid4().hex}_{name_hint}" + + async def _stage_workspace_copy(self) -> tuple[Path, Path]: + root = Path(self.state.manifest.root) + root_name = root.name or "workspace" + staging_parent = self._archive_stage_path(name_hint="workspace") + staging_workspace = staging_parent / root_name + + await self._exec_checked( + "mkdir", + "-p", + str(staging_parent), + error_cls=WorkspaceArchiveReadError, + error_path=root, + ) + await self._exec_checked( + "cp", + "-R", + "--", + str(root), + str(staging_workspace), + error_cls=WorkspaceArchiveReadError, + error_path=root, + ) + return staging_parent, staging_workspace + + async def _rm_best_effort(self, path: Path) -> None: + try: + await self.exec("rm", "-rf", "--", str(path), shell=False) + except Exception: + pass + + async def _exec_checked( + self, + *cmd: str | Path, + error_cls: type[WorkspaceArchiveReadError] | type[WorkspaceArchiveWriteError], + error_path: Path, + ) -> ExecResult: + res = await self.exec(*cmd, shell=False) + if not res.ok(): + raise error_cls( + path=error_path, + context={ + "command": [str(c) for c in cmd], + "stdout": res.stdout.decode("utf-8", errors="replace"), + "stderr": res.stderr.decode("utf-8", errors="replace"), + }, + ) + return res + + async def start(self) -> None: + self._container.reload() + if not await self.running(): + self._container.start() + await super().start() + self._workspace_root_ready = True + self.state.workspace_root_ready = True + self._resume_workspace_probe_pending = False + + async def _exec_run( + self, + *, + cmd: list[str], + workdir: str | None, + timeout: float | None, + command_for_errors: tuple[str | Path, ...], + kill_on_timeout: bool, + ) -> ExecResult: + loop = asyncio.get_running_loop() + future = loop.run_in_executor( + _DOCKER_EXECUTOR, + lambda: self._container.exec_run(cmd=cmd, demux=True, workdir=workdir), + ) + try: + exec_result = await asyncio.wait_for(future, timeout=timeout) + except asyncio.TimeoutError as e: + if kill_on_timeout: + # Best-effort: kill processes matching the command line. + # If this fails, the caller still gets a timeout error. + try: + pattern = " ".join(str(c) for c in command_for_errors).replace("'", "'\\''") + self._container.exec_run( + cmd=[ + "sh", + "-lc", + f"pkill -f -- '{pattern}' >/dev/null 2>&1 || true", + ], + demux=True, + ) + except Exception: + pass + raise ExecTimeoutError(command=command_for_errors, timeout_s=timeout, cause=e) from e + except Exception as e: + raise ExecTransportError(command=command_for_errors, cause=e) from e + + stdout, stderr = exec_result.output + return ExecResult( + stdout=stdout or b"", + stderr=stderr or b"", + exit_code=exec_result.exit_code or 0, + ) + + async def _recover_workspace_root_ready(self, *, timeout: float | None) -> None: + if self._workspace_root_ready or not self._resume_workspace_probe_pending: + return + + root = self.state.manifest.root + probe_command = ("test", "-d", "--", root) + try: + result = await self._exec_run( + cmd=[str(c) for c in probe_command], + workdir=None, + timeout=timeout, + command_for_errors=probe_command, + kill_on_timeout=False, + ) + except (ExecTimeoutError, ExecTransportError): + return + finally: + self._resume_workspace_probe_pending = False + + if result.ok(): + self._workspace_root_ready = True + self.state.workspace_root_ready = True + + async def _exec_internal( + self, *command: str | Path, timeout: float | None = None + ) -> ExecResult: + # `docker-py` is synchronous and can block indefinitely (e.g. hung + # process, daemon issues). Run in a worker thread so we can enforce a + # timeout without requiring `timeout(1)` in the container image. + # Use a shared bounded executor so repeated timeouts do not leak one + # new thread per command. + cmd: list[str] = [str(c) for c in command] + await self._recover_workspace_root_ready(timeout=timeout) + # The workspace root is created during `apply_manifest()`, so the first + # bootstrap commands must not force Docker to chdir there yet. + workdir = self.state.manifest.root if self._workspace_root_ready else None + return await self._exec_run( + cmd=cmd, + workdir=workdir, + timeout=timeout, + command_for_errors=command, + kill_on_timeout=True, + ) + + async def read(self, path: Path) -> io.IOBase: + workspace_path = resolve_workspace_path( + Path(self.state.manifest.root), + path, + allow_absolute_within_root=True, + ) + + # Docker's archive APIs (put/get) can be flaky for paths that exist *inside* the container + # but are not visible to the Docker daemon (notably FUSE mounts like mount-s3/rclone). + # Mirror `write()`: always stage into a daemon-visible directory, then `get_archive` there. + staging_path = self._archive_stage_path(name_hint=workspace_path.name) + + await self._exec_checked( + "mkdir", + "-p", + str(self._ARCHIVE_STAGING_DIR), + error_cls=WorkspaceArchiveReadError, + error_path=path, + ) + + cp_res = await self.exec("cp", "--", str(workspace_path), str(staging_path), shell=False) + if not cp_res.ok(): + # Best-effort: treat stage failure as not-found. (It can also be permissions, but we + # don't have a dedicated error type for that yet.) + raise WorkspaceReadNotFoundError( + path=path, + context={ + "command": ["cp", "--", str(workspace_path), str(staging_path)], + "stdout": cp_res.stdout.decode("utf-8", errors="replace"), + "stderr": cp_res.stderr.decode("utf-8", errors="replace"), + }, + ) + + try: + stream, _ = self._container.get_archive(str(staging_path)) + except docker.errors.NotFound as e: + raise WorkspaceReadNotFoundError(path=path, cause=e) from e + except docker.errors.APIError as e: + raise WorkspaceArchiveReadError(path=path, cause=e) from e + finally: + # Best-effort cleanup. + await self._rm_best_effort(staging_path) + + # `get_archive` returns a tar stream. For a single-file read we buffer + # the tar bytes so tarfile can operate in non-streaming mode (seeking + # is required by some reads). + try: + raw = b"".join(stream) + with tarfile.open(fileobj=io.BytesIO(raw), mode="r:*") as tar: + members = tar.getmembers() + if not members: + raise WorkspaceReadNotFoundError(path=path) + extracted = tar.extractfile(members[0]) + if extracted is None: + raise WorkspaceReadNotFoundError(path=path) + return io.BytesIO(extracted.read()) + except WorkspaceReadNotFoundError: + raise + except (tarfile.TarError, OSError) as e: + raise WorkspaceArchiveReadError(path=path, cause=e) from e + + async def write(self, path: Path, data: io.IOBase) -> None: + # Buffer the file first so we can set TarInfo.size correctly. + payload = coerce_write_payload(path=path, data=data) + + path = resolve_workspace_path( + Path(self.state.manifest.root), + path, + allow_absolute_within_root=True, + ) + + parent = path.parent + await self.mkdir(parent, parents=True) + + # Docker's archive APIs (put/get) can be flaky for paths that exist *inside* the container + # but are not visible to the Docker daemon (notably FUSE mounts like mount-s3/rclone). + # To make writes robust across normal dirs and mountpoints, always stage the payload in + # a daemon-visible directory and then copy into place from inside the container. + staging_path = self._archive_stage_path(name_hint=path.name) + staging_name = staging_path.name + + await self._exec_checked( + "mkdir", + "-p", + str(self._ARCHIVE_STAGING_DIR), + error_cls=WorkspaceArchiveWriteError, + error_path=self._ARCHIVE_STAGING_DIR, + ) + + info = tarfile.TarInfo(name=staging_name) + info.size = len(payload) + + tar_buf = io.BytesIO() + with tarfile.open(fileobj=tar_buf, mode="w") as tar: + tar.addfile(info, io.BytesIO(bytes(payload))) + + tar_buf.seek(0) + try: + self._container.put_archive(str(self._ARCHIVE_STAGING_DIR), tar_buf) + except docker.errors.APIError as e: + raise WorkspaceArchiveWriteError(path=self._ARCHIVE_STAGING_DIR, cause=e) from e + + # Copy into place using a process inside the container, which can see mounts. + cp_res = await self.exec("cp", "--", str(staging_path), str(path), shell=False) + if not cp_res.ok(): + raise WorkspaceArchiveWriteError( + path=parent, + context={ + "command": ["cp", "--", str(staging_path), str(path)], + "stdout": cp_res.stdout.decode("utf-8", errors="replace"), + "stderr": cp_res.stderr.decode("utf-8", errors="replace"), + }, + ) + + # Best-effort cleanup. Ignore failures (e.g. concurrent cleanup). + await self._rm_best_effort(staging_path) + + async def running(self) -> bool: + # docker-py caches container attributes; refresh to avoid stale status, + # especially right after start/stop. + try: + self._container.reload() + except docker.errors.APIError: + # Best-effort: if we can't reload, fall back to last known status. + pass + return cast(str, self._container.status) == "running" + + async def stop(self) -> None: + # Persistence-only. Container teardown is handled in `shutdown()`. + await super().stop() + + async def shutdown(self) -> None: + # Best-effort: stop the container if it exists. + try: + self._container.reload() + except Exception: + pass + try: + if await self.running(): + self._container.stop() + except Exception: + # If the container is already gone/stopped, ignore. + pass + + def should_provision_manifest_accounts_on_resume(self) -> bool: + return not self._resume_preserves_system_state + + async def exists(self) -> bool: + try: + self._docker_client.containers.get(self.state.container_id) + return True + except docker.errors.NotFound: + return False + + @retry_async( + retry_if=lambda exc, self: exception_chain_has_status_code(exc, TRANSIENT_HTTP_STATUS_CODES) + ) + async def persist_workspace(self) -> io.IOBase: + skip = self.state.manifest.ephemeral_entry_paths() + root = Path(self.state.manifest.root) + staging_parent, staging_workspace = await self._stage_workspace_copy() + + try: + for rel_path in skip: + await self._rm_best_effort(staging_workspace / rel_path) + + bits, _ = self._container.get_archive(str(staging_workspace)) + root_name = root.name or "workspace" + if not skip: + return IteratorIO(it=bits) + + in_stream = IteratorIO(it=bits) + out_stream = tempfile.SpooledTemporaryFile(max_size=16 * 1024 * 1024, mode="w+b") + try: + with ( + tarfile.open(fileobj=in_stream, mode="r|*") as in_tar, + tarfile.open(fileobj=out_stream, mode="w") as out_tar, + ): + for member in in_tar: + if should_skip_tar_member( + member.name, skip_rel_paths=skip, root_name=root_name + ): + continue + fileobj = in_tar.extractfile(member) if member.isreg() else None + out_tar.addfile(member, fileobj) + if fileobj is not None: + fileobj.close() + except (tarfile.TarError, OSError) as e: + out_stream.close() + raise WorkspaceArchiveReadError(path=root, cause=e) from e + + out_stream.seek(0) + return cast(io.IOBase, out_stream) + except docker.errors.NotFound as e: + raise WorkspaceArchiveReadError(path=root, cause=e) from e + except docker.errors.APIError as e: + raise WorkspaceArchiveReadError(path=root, cause=e) from e + finally: + await self._rm_best_effort(staging_parent) + + async def hydrate_workspace(self, data: io.IOBase) -> None: + root = self.state.manifest.root + hydration_target = Path(root).parent + try: + ok = self._container.put_archive(str(hydration_target), data) + except docker.errors.APIError as e: + raise WorkspaceArchiveWriteError(path=Path(root), cause=e) from e + if not ok: + raise WorkspaceArchiveWriteError( + path=Path(root), context={"reason": "put_archive_returned_false"} + ) + + +class DockerSandboxClient(BaseSandboxClient[DockerSandboxClientOptions]): + backend_id = "docker" + docker_client: DockerSDKClient + _instrumentation: Instrumentation + + def __init__( + self, + docker_client: DockerSDKClient, + *, + instrumentation: Instrumentation | None = None, + dependencies: Dependencies | None = None, + ) -> None: + super().__init__() + self.docker_client = docker_client + self._instrumentation = instrumentation or Instrumentation() + self._dependencies = dependencies + + async def create( + self, + *, + snapshot: SnapshotSpec | None = None, + manifest: Manifest | None = None, + options: DockerSandboxClientOptions, + ) -> SandboxSession: + image = options.image + + container = await self._create_container(image, manifest=manifest) + container.start() + + session_id = uuid.uuid4() + container_id = container.id + assert container_id is not None + snapshot_id = str(session_id) + snapshot_instance = resolve_snapshot(snapshot, snapshot_id) + state = DockerSandboxSessionState( + session_id=session_id, + manifest=manifest or Manifest(), + image=image, + snapshot=snapshot_instance, + container_id=container_id, + ) + + inner = DockerSandboxSession( + docker_client=self.docker_client, + container=container, + state=state, + ) + return self._wrap_session(inner, instrumentation=self._instrumentation) + + async def delete(self, session: SandboxSession) -> SandboxSession: + inner = session._inner + if not isinstance(inner, DockerSandboxSession): + raise TypeError("DockerSandboxClient.delete expects a DockerSandboxSession") + try: + container = self.docker_client.containers.get(inner.state.container_id) + except docker.errors.NotFound: + return session + # Ensure teardown happens before removal. + try: + await inner.shutdown() + except Exception: + pass + try: + container.remove() + except docker.errors.NotFound: + return session + return session + + async def resume(self, state: SandboxSessionState) -> SandboxSession: + if not isinstance(state, DockerSandboxSessionState): + raise TypeError("DockerSandboxClient.resume expects a DockerSandboxSessionState") + container = self.get_container(state.container_id) + reused_existing_container = container is not None + if container is None: + container = await self._create_container(state.image, manifest=state.manifest) + container_id = container.id + assert container_id is not None + state.container_id = container_id + state.workspace_root_ready = False + + # Use the existing container (or the one we just created). + inner = DockerSandboxSession( + container=container, docker_client=self.docker_client, state=state + ) + inner._resume_workspace_probe_pending = True + inner._resume_preserves_system_state = reused_existing_container + return self._wrap_session(inner, instrumentation=self._instrumentation) + + def deserialize_session_state(self, payload: dict[str, object]) -> SandboxSessionState: + return DockerSandboxSessionState.model_validate(payload) + + async def _create_container(self, image: str, *, manifest: Manifest | None = None) -> Container: + # create image if it does not exist + if not self.image_exists(image): + repo, tag = parse_repository_tag(image) + self.docker_client.images.pull(repo, tag=tag or None, all_tags=False) + + assert self.image_exists(image) + environment: dict[str, str] | None = None + if manifest: + environment = await manifest.environment.resolve() + create_kwargs: dict[str, object] = { + "entrypoint": ["tail"], + "image": image, + "detach": True, + "command": ["-f", "/dev/null"], + "environment": environment, + } + if _manifest_requires_fuse(manifest): + create_kwargs.update( + devices=["/dev/fuse"], + cap_add=["SYS_ADMIN"], + security_opt=["apparmor:unconfined"], + ) + elif _manifest_requires_sys_admin(manifest): + create_kwargs.update( + cap_add=["SYS_ADMIN"], + security_opt=["apparmor:unconfined"], + ) + return self.docker_client.containers.create(**create_kwargs) + + def image_exists(self, image: str) -> bool: + try: + self.docker_client.images.get(image) + return True + except docker.errors.ImageNotFound: + return False + + def get_container(self, container_id: str) -> Container | None: + try: + return self.docker_client.containers.get(container_id) + except docker.errors.NotFound: + return None + + +def _manifest_requires_fuse(manifest: Manifest | None) -> bool: + if manifest is None: + return False + for _path, artifact in manifest.iter_entries(): + if isinstance(artifact, Mount): + mount_pattern = getattr(artifact, "mount_pattern", None) + if isinstance(mount_pattern, (FuseMountPattern, MountpointMountPattern)): + return True + if isinstance(mount_pattern, RcloneMountPattern) and mount_pattern.mode == "fuse": + return True + return False + + +def _manifest_requires_sys_admin(manifest: Manifest | None) -> bool: + if manifest is None: + return False + for _path, artifact in manifest.iter_entries(): + if isinstance(artifact, Mount): + mount_pattern = getattr(artifact, "mount_pattern", None) + if isinstance(mount_pattern, RcloneMountPattern) and mount_pattern.mode == "nfs": + return True + return False diff --git a/src/agents/sandbox/sandboxes/unix_local.py b/src/agents/sandbox/sandboxes/unix_local.py new file mode 100644 index 0000000000..ce6ca8dcd0 --- /dev/null +++ b/src/agents/sandbox/sandboxes/unix_local.py @@ -0,0 +1,568 @@ +import asyncio +import io +import logging +import os +import shlex +import shutil +import signal +import sys +import tarfile +import tempfile +import uuid +from collections.abc import Mapping, Sequence +from pathlib import Path +from typing import Literal, cast + +from ..entries import resolve_workspace_path +from ..errors import ( + ExecNonZeroError, + ExecTimeoutError, + ExecTransportError, + InvalidManifestPathError, + WorkspaceArchiveReadError, + WorkspaceArchiveWriteError, + WorkspaceReadNotFoundError, + WorkspaceRootNotFoundError, + WorkspaceStartError, + WorkspaceStopError, +) +from ..files import EntryKind, FileEntry +from ..manifest import Manifest +from ..materialization import MaterializationResult +from ..session import SandboxSession, SandboxSessionState +from ..session.base_sandbox_session import BaseSandboxSession +from ..session.dependencies import Dependencies +from ..session.manager import Instrumentation +from ..session.sandbox_client import BaseSandboxClient +from ..session.workspace_payloads import coerce_write_payload +from ..snapshot import SnapshotSpec, resolve_snapshot +from ..types import ExecResult, Permissions, User +from ..util.tar_utils import ( + UnsafeTarMemberError, + safe_extract_tarfile, + should_skip_tar_member, +) + +_DEFAULT_WORKSPACE_PREFIX = "uc-local-" +_DEFAULT_MANIFEST_ROOT = cast(str, Manifest.model_fields["root"].default) + +logger = logging.getLogger(__name__) + + +class UnixLocalSandboxSessionState(SandboxSessionState): + workspace_root_owned: bool = False + + +class UnixLocalSandboxSession(BaseSandboxSession): + """ + Unix-only session implementation that runs commands on the host and uses the host filesystem + as the workspace (rooted at `self.state.manifest.root`). + """ + + state: UnixLocalSandboxSessionState + _running: bool + + def __init__(self, *, state: UnixLocalSandboxSessionState) -> None: + self.state = state + self._running = False + + @classmethod + def from_state(cls, state: UnixLocalSandboxSessionState) -> "UnixLocalSandboxSession": + return cls(state=state) + + async def start(self) -> None: + workspace = Path(self.state.manifest.root) + try: + workspace.mkdir(parents=True, exist_ok=True) + except OSError as e: + raise WorkspaceStartError(path=workspace, cause=e) from e + + self._running = True + await super().start() + + async def stop(self) -> None: + try: + await super().stop() + except Exception as e: + raise WorkspaceStopError(path=Path(self.state.manifest.root), cause=e) from e + + async def apply_manifest(self, *, only_ephemeral: bool = False) -> MaterializationResult: + if self.state.manifest.users or self.state.manifest.groups: + raise ValueError( + "UnixLocalSandboxSession does not support manifest users or groups because " + "provisioning would run on the host machine" + ) + return await super().apply_manifest(only_ephemeral=only_ephemeral) + + async def provision_manifest_accounts(self) -> None: + if self.state.manifest.users or self.state.manifest.groups: + raise ValueError( + "UnixLocalSandboxSession does not support manifest users or groups because " + "provisioning would run on the host machine" + ) + + async def shutdown(self) -> None: + # Best-effort: mark session not running. We intentionally do not delete the workspace + # directory here; cleanup is handled by the Client.delete(). + self._running = False + + def _prepare_exec_command( + self, + *command: str | Path, + shell: bool | list[str], + user: str | User | None, + ) -> list[str]: + if shell is True: + shell = ["sh", "-c"] + return super()._prepare_exec_command(*command, shell=shell, user=user) + + async def _exec_internal( + self, *command: str | Path, timeout: float | None = None + ) -> ExecResult: + env, cwd = await self._resolved_exec_context() + workspace_root = Path(cwd).resolve() + command_parts = self._workspace_relative_command_parts(command, workspace_root) + process_cwd, command_parts = self._shell_workspace_process_context( + command_parts=command_parts, + workspace_root=workspace_root, + cwd=cwd, + ) + exec_command = self._confined_exec_command( + command_parts=command_parts, + workspace_root=workspace_root, + env=env, + ) + + try: + proc = await asyncio.create_subprocess_exec( + *exec_command, + stdout=asyncio.subprocess.PIPE, + stderr=asyncio.subprocess.PIPE, + cwd=process_cwd, + env=env, + start_new_session=True, + ) + + try: + stdout, stderr = await asyncio.wait_for(proc.communicate(), timeout=timeout) + except asyncio.TimeoutError as e: + try: + # process tree cleanup + os.killpg(proc.pid, signal.SIGKILL) + except Exception: + pass + raise ExecTimeoutError(command=command, timeout_s=timeout, cause=e) from e + except ExecTimeoutError: + raise + except Exception as e: + raise ExecTransportError(command=command, cause=e) from e + + return ExecResult( + stdout=stdout or b"", stderr=stderr or b"", exit_code=proc.returncode or 0 + ) + + async def _resolved_exec_context(self) -> tuple[dict[str, str], str]: + env = os.environ.copy() + env.update(await self.state.manifest.environment.resolve()) + + workspace = Path(self.state.manifest.root) + if not workspace.exists(): + raise WorkspaceRootNotFoundError(path=workspace) + + env["HOME"] = str(workspace) + return env, str(workspace) + + def _confined_exec_command( + self, + *, + command_parts: list[str], + workspace_root: Path, + env: Mapping[str, str], + ) -> list[str]: + if sys.platform != "darwin": + return command_parts + + sandbox_exec = shutil.which("sandbox-exec") + if not sandbox_exec: + raise ExecTransportError( + command=command_parts, + context={ + "reason": "unix_local_confinement_unavailable", + "platform": sys.platform, + "workspace_root": str(workspace_root), + }, + ) + + profile = self._darwin_exec_profile( + workspace_root, + extra_read_paths=self._darwin_additional_read_paths( + command_parts=command_parts, + env=env, + ), + ) + return [sandbox_exec, "-p", profile, *command_parts] + + @staticmethod + def _workspace_relative_command_parts( + command: tuple[str | Path, ...], + workspace_root: Path, + ) -> list[str]: + command_parts = [str(part) for part in command] + rewritten = [command_parts[0]] + for part in command_parts[1:]: + path_part = Path(part) + if not path_part.is_absolute(): + rewritten.append(part) + continue + try: + relative = path_part.relative_to(workspace_root) + except ValueError: + rewritten.append(part) + continue + rewritten.append("." if not relative.parts else relative.as_posix()) + return rewritten + + @staticmethod + def _darwin_allowable_read_roots(path: Path, *, host_home: Path) -> list[Path]: + candidates: set[Path] = set() + normalized = path.expanduser() + try: + resolved = normalized.resolve(strict=False) + except OSError: + resolved = normalized + + if normalized.is_dir(): + candidates.add(normalized) + else: + candidates.add(normalized.parent) + + if resolved.is_dir(): + candidates.add(resolved) + else: + candidates.add(resolved.parent) + + resolved_text = resolved.as_posix() + if resolved_text == "/opt/homebrew" or resolved_text.startswith("/opt/homebrew/"): + candidates.add(Path("/opt/homebrew")) + if resolved_text == "/usr/local" or resolved_text.startswith("/usr/local/"): + candidates.add(Path("/usr/local")) + if resolved_text == "/Library/Frameworks" or resolved_text.startswith( + "/Library/Frameworks/" + ): + candidates.add(Path("/Library/Frameworks")) + + try: + relative_to_home = resolved.relative_to(host_home) + except ValueError: + relative_to_home = None + if relative_to_home is not None and relative_to_home.parts: + first_segment = relative_to_home.parts[0] + if first_segment.startswith("."): + candidates.add(host_home / first_segment) + elif len(relative_to_home.parts) >= 2 and relative_to_home.parts[:2] == ( + "Library", + "Python", + ): + candidates.add(host_home / "Library" / "Python") + + return sorted( + candidates, key=lambda candidate: (len(candidate.parts), candidate.as_posix()) + ) + + def _darwin_additional_read_paths( + self, + *, + command_parts: list[str], + env: Mapping[str, str], + ) -> list[Path]: + host_home = Path.home().resolve() + allowed: list[Path] = [] + seen: set[str] = set() + + def _append(path: str | Path | None) -> None: + if path is None: + return + candidate = Path(path).expanduser() + if not candidate.is_absolute(): + return + for root in self._darwin_allowable_read_roots(candidate, host_home=host_home): + key = root.as_posix() + if key in seen: + continue + seen.add(key) + allowed.append(root) + + for path_entry in env.get("PATH", "").split(os.pathsep): + if path_entry: + _append(path_entry) + + executable = shutil.which(command_parts[0], path=env.get("PATH")) + _append(executable) + return allowed + + def _darwin_exec_profile( + self, + workspace_root: Path, + *, + extra_read_paths: Sequence[Path] = (), + ) -> str: + def _literal(path: Path | str) -> str: + escaped = str(path).replace("\\", "\\\\").replace('"', '\\"') + return f'"{escaped}"' + + denied_paths = [ + Path("/Users"), + Path("/Volumes"), + Path("/Applications"), + Path("/Library"), + Path("/opt"), + Path("/etc"), + Path("/private/etc"), + Path("/tmp"), + Path("/private/tmp"), + Path("/private"), + Path("/var"), + Path("/usr"), + ] + allow_rules = [ + f"(allow file-read-data file-read-metadata (subpath {_literal(workspace_root)}))", + f"(allow file-write* (subpath {_literal(workspace_root)}))", + *[ + f"(allow file-read-data file-read-metadata (subpath {_literal(path)}))" + for path in extra_read_paths + ], + '(allow file-read-data file-read-metadata (subpath "/usr/bin"))', + '(allow file-read-data file-read-metadata (subpath "/usr/lib"))', + '(allow file-read-data file-read-metadata (subpath "/bin"))', + '(allow file-read-data file-read-metadata (subpath "/System"))', + '(allow file-read-data file-read-metadata (literal "/private/var/select/sh"))', + '(allow file-write* (literal "/dev/null"))', + ] + deny_rules = "\n".join( + f"(deny file-read-data (subpath {_literal(path)}))\n" + f"(deny file-write* (subpath {_literal(path)}))" + for path in denied_paths + ) + return "\n".join( + [ + "(version 1)", + "(allow default)", + deny_rules, + *allow_rules, + ] + ) + + @staticmethod + def _shell_workspace_process_context( + *, + command_parts: list[str], + workspace_root: Path, + cwd: str, + ) -> tuple[str, list[str]]: + if len(command_parts) < 3 or command_parts[0] != "sh" or command_parts[1] != "-c": + return cwd, command_parts + + workspace_cd = f"cd {shlex.quote(str(workspace_root))} && {command_parts[2]}" + rewritten = [*command_parts] + rewritten[2] = workspace_cd + return "/", rewritten + + def _resolve_workspace_path(self, path: Path) -> Path: + workspace_root = Path(self.state.manifest.root).resolve() + confined = resolve_workspace_path( + workspace_root, + path, + allow_absolute_within_root=True, + ) + resolved = confined.resolve(strict=False) + try: + resolved.relative_to(workspace_root) + except ValueError as exc: + reason: Literal["absolute", "escape_root"] = ( + "absolute" if path.is_absolute() else "escape_root" + ) + raise InvalidManifestPathError(rel=path, reason=reason, cause=exc) from exc + return resolved + + def normalize_path(self, path: Path | str) -> Path: + if isinstance(path, str): + path = Path(path) + return self._resolve_workspace_path(path) + + async def ls(self, path: Path | str) -> list[FileEntry]: + normalized = self.normalize_path(path) + command = ("ls", "-la", "--", str(normalized)) + try: + with os.scandir(normalized) as entries: + listed: list[FileEntry] = [] + for entry in entries: + stat_result = entry.stat(follow_symlinks=False) + if entry.is_symlink(): + kind = EntryKind.SYMLINK + elif entry.is_dir(follow_symlinks=False): + kind = EntryKind.DIRECTORY + elif entry.is_file(follow_symlinks=False): + kind = EntryKind.FILE + else: + kind = EntryKind.OTHER + listed.append( + FileEntry( + path=entry.path, + permissions=Permissions.from_mode(stat_result.st_mode), + owner=str(stat_result.st_uid), + group=str(stat_result.st_gid), + size=stat_result.st_size, + kind=kind, + ) + ) + return listed + except OSError as e: + raise ExecNonZeroError( + ExecResult(stdout=b"", stderr=str(e).encode("utf-8"), exit_code=1), + command=command, + cause=e, + ) from e + + async def mkdir(self, path: Path | str, *, parents: bool = False) -> None: + normalized = self.normalize_path(path) + workspace_root = Path(self.state.manifest.root).resolve() + if parents and normalized == workspace_root: + workspace_root.mkdir(parents=True, exist_ok=True) + return + await super().mkdir(normalized, parents=parents) + + async def read(self, path: Path) -> io.IOBase: + workspace_path = self._resolve_workspace_path(path) + try: + return workspace_path.open("rb") + except FileNotFoundError as e: + raise WorkspaceReadNotFoundError(path=path, cause=e) from e + except OSError as e: + raise WorkspaceArchiveReadError(path=path, cause=e) from e + + async def write(self, path: Path, data: io.IOBase) -> None: + payload = coerce_write_payload(path=path, data=data) + + workspace_path = self._resolve_workspace_path(path) + try: + workspace_path.parent.mkdir(parents=True, exist_ok=True) + workspace_path.write_bytes(payload) + except OSError as e: + raise WorkspaceArchiveWriteError(path=workspace_path, cause=e) from e + + async def running(self) -> bool: + return self._running + + async def persist_workspace(self) -> io.IOBase: + root = Path(self.state.manifest.root) + if not root.exists(): + raise WorkspaceArchiveReadError( + path=root, context={"reason": "workspace_root_not_found"} + ) + + skip = self.state.manifest.ephemeral_entry_paths() + buf = io.BytesIO() + try: + with tarfile.open(fileobj=buf, mode="w") as tar: + tar.add( + root, + arcname=".", + filter=lambda ti: ( + None + if should_skip_tar_member( + ti.name, + skip_rel_paths=skip, + root_name=None, + ) + else ti + ), + ) + except (tarfile.TarError, OSError) as e: + raise WorkspaceArchiveReadError(path=root, cause=e) from e + + buf.seek(0) + return buf + + async def hydrate_workspace(self, data: io.IOBase) -> None: + root = Path(self.state.manifest.root) + try: + root.mkdir(parents=True, exist_ok=True) + with tarfile.open(fileobj=data, mode="r:*") as tar: + safe_extract_tarfile(tar, root=root) + except UnsafeTarMemberError as e: + raise WorkspaceArchiveWriteError( + path=root, context={"reason": e.reason, "member": e.member}, cause=e + ) from e + except (tarfile.TarError, OSError) as e: + raise WorkspaceArchiveWriteError(path=root, cause=e) from e + + +class UnixLocalSandboxClient(BaseSandboxClient[None]): + backend_id = "unix_local" + supports_default_options = True + _instrumentation: Instrumentation + + def __init__( + self, + *, + instrumentation: Instrumentation | None = None, + dependencies: Dependencies | None = None, + ) -> None: + self._instrumentation = instrumentation or Instrumentation() + self._dependencies = dependencies + + async def create( + self, + *, + snapshot: SnapshotSpec | None = None, + manifest: Manifest | None = None, + options: None = None, + ) -> SandboxSession: + if options is not None: + raise ValueError("UnixLocalSandboxClient.create does not accept options") + # For local execution, runner-created sessions should always get an isolated temp root + # unless the caller explicitly chose a custom host path. + workspace_root_owned = False + if manifest is None or manifest.root == _DEFAULT_MANIFEST_ROOT: + workspace_dir = tempfile.mkdtemp(prefix=_DEFAULT_WORKSPACE_PREFIX) + workspace_root_owned = True + if manifest is None: + manifest = Manifest(root=workspace_dir) + else: + manifest = manifest.model_copy(update={"root": workspace_dir}, deep=True) + + session_id = uuid.uuid4() + snapshot_id = str(session_id) + snapshot_instance = resolve_snapshot(snapshot, snapshot_id) + state = UnixLocalSandboxSessionState( + session_id=session_id, + manifest=manifest, + snapshot=snapshot_instance, + workspace_root_owned=workspace_root_owned, + ) + inner = UnixLocalSandboxSession.from_state(state) + return self._wrap_session(inner, instrumentation=self._instrumentation) + + async def delete(self, session: SandboxSession) -> SandboxSession: + """Best-effort cleanup of the on-disk workspace directory.""" + inner = session._inner + if not isinstance(inner, UnixLocalSandboxSession): + raise TypeError("UnixLocalSandboxClient.delete expects a UnixLocalSandboxSession") + if not inner.state.workspace_root_owned: + return session + try: + shutil.rmtree(Path(inner.state.manifest.root), ignore_errors=False) + except FileNotFoundError: + pass + except Exception: + pass + return session + + async def resume(self, state: SandboxSessionState) -> SandboxSession: + if not isinstance(state, UnixLocalSandboxSessionState): + raise TypeError("UnixLocalSandboxClient.resume expects a UnixLocalSandboxSessionState") + inner = UnixLocalSandboxSession.from_state(state) + return self._wrap_session(inner, instrumentation=self._instrumentation) + + def deserialize_session_state(self, payload: dict[str, object]) -> SandboxSessionState: + return UnixLocalSandboxSessionState.model_validate(payload) diff --git a/src/agents/sandbox/session/__init__.py b/src/agents/sandbox/session/__init__.py new file mode 100644 index 0000000000..738e21b799 --- /dev/null +++ b/src/agents/sandbox/session/__init__.py @@ -0,0 +1,118 @@ +from __future__ import annotations + +from typing import TYPE_CHECKING + +__all__ = [ + "BaseSandboxClient", + "BaseSandboxSession", + "CallbackSink", + "ChainedSink", + "ClientOptionsT", + "Dependencies", + "DependenciesBindingError", + "DependenciesError", + "DependenciesMissingDependencyError", + "DependencyKey", + "EventPayloadPolicy", + "EventSink", + "HttpProxySink", + "Instrumentation", + "JsonlOutboxSink", + "SandboxSession", + "SandboxSessionState", + "UCEvent", + "UCFinishEvent", + "UCStartEvent", + "WorkspaceJsonlSink", + "event_to_json_line", + "validate_uc_event", +] + +if TYPE_CHECKING: + from .base_sandbox_session import BaseSandboxSession + from .dependencies import ( + Dependencies, + DependenciesBindingError, + DependenciesError, + DependenciesMissingDependencyError, + DependencyKey, + ) + from .events import ( + EventPayloadPolicy, + UCEvent, + UCFinishEvent, + UCStartEvent, + validate_uc_event, + ) + from .manager import Instrumentation + from .sandbox_client import BaseSandboxClient, ClientOptionsT + from .sandbox_session import SandboxSession + from .sandbox_session_state import SandboxSessionState + from .sinks import ( + CallbackSink, + ChainedSink, + EventSink, + HttpProxySink, + JsonlOutboxSink, + WorkspaceJsonlSink, + ) + from .utils import event_to_json_line + + +def __getattr__(name: str) -> object: + if name == "BaseSandboxSession": + from .base_sandbox_session import BaseSandboxSession + + return BaseSandboxSession + if name in { + "Dependencies", + "DependenciesBindingError", + "DependenciesError", + "DependenciesMissingDependencyError", + "DependencyKey", + }: + from . import dependencies as dependencies_module + + return getattr(dependencies_module, name) + if name in { + "EventPayloadPolicy", + "UCEvent", + "UCFinishEvent", + "UCStartEvent", + "validate_uc_event", + }: + from . import events as events_module + + return getattr(events_module, name) + if name == "Instrumentation": + from .manager import Instrumentation + + return Instrumentation + if name in {"BaseSandboxClient", "ClientOptionsT"}: + from . import sandbox_client as sandbox_client_module + + return getattr(sandbox_client_module, name) + if name == "SandboxSession": + from .sandbox_session import SandboxSession + + return SandboxSession + if name == "SandboxSessionState": + from .sandbox_session_state import SandboxSessionState + + return SandboxSessionState + if name in { + "CallbackSink", + "ChainedSink", + "EventSink", + "HttpProxySink", + "JsonlOutboxSink", + "WorkspaceJsonlSink", + }: + from . import sinks as sinks_module + + return getattr(sinks_module, name) + if name == "event_to_json_line": + from .utils import event_to_json_line + + return event_to_json_line + raise AttributeError(f"module {__name__!r} has no attribute {name!r}") diff --git a/src/agents/sandbox/session/archive_extraction.py b/src/agents/sandbox/session/archive_extraction.py new file mode 100644 index 0000000000..ca219661d2 --- /dev/null +++ b/src/agents/sandbox/session/archive_extraction.py @@ -0,0 +1,299 @@ +from __future__ import annotations + +import io +import tarfile +import zipfile +from collections.abc import Awaitable, Callable +from pathlib import Path, PurePosixPath +from typing import Literal, cast + +from ..errors import ExecNonZeroError, WorkspaceArchiveWriteError +from ..files import EntryKind, FileEntry +from ..util.tar_utils import UnsafeTarMemberError, safe_tar_member_rel_path + + +class UnsafeZipMemberError(ValueError): + """Raised when a zip member would escape or violate archive extraction rules.""" + + def __init__(self, *, member: str, reason: str) -> None: + super().__init__(f"unsafe zip member {member!r}: {reason}") + self.member = member + self.reason = reason + + +class WorkspaceArchiveExtractor: + def __init__( + self, + *, + mkdir: Callable[[Path], Awaitable[None]], + write: Callable[[Path, io.IOBase], Awaitable[None]], + ls: Callable[[Path], Awaitable[list[FileEntry]]], + ) -> None: + self._mkdir = mkdir + self._write = write + self._ls = ls + + async def extract_tar_archive( + self, + *, + archive_path: Path, + destination_root: Path, + data: io.IOBase, + ) -> None: + child_entry_cache: dict[Path, dict[str, EntryKind]] = {} + try: + with tarfile.open(fileobj=data, mode="r:*") as archive: + for member in archive.getmembers(): + rel_path = safe_tar_member_rel_path(member) + if rel_path is None: + continue + + await self._ensure_no_symlink_extract_parents( + destination_root=destination_root, + rel_path=rel_path, + member_name=member.name, + error_type="tar", + child_entry_cache=child_entry_cache, + ) + dest = destination_root / rel_path + if member.isdir(): + await self._mkdir(dest) + self._record_extract_entry( + child_entry_cache=child_entry_cache, + destination_root=destination_root, + path=dest, + kind=EntryKind.DIRECTORY, + ) + continue + + fileobj = archive.extractfile(member) + if fileobj is None: + raise UnsafeTarMemberError( + member=member.name, + reason="missing file payload", + ) + try: + await self._mkdir(dest.parent) + self._record_extract_entry( + child_entry_cache=child_entry_cache, + destination_root=destination_root, + path=dest.parent, + kind=EntryKind.DIRECTORY, + ) + await self._write(dest, cast(io.IOBase, fileobj)) + self._record_extract_entry( + child_entry_cache=child_entry_cache, + destination_root=destination_root, + path=dest, + kind=EntryKind.FILE, + ) + finally: + fileobj.close() + except UnsafeTarMemberError as e: + raise WorkspaceArchiveWriteError( + path=archive_path, + context={"member": e.member, "reason": e.reason}, + cause=e, + ) from e + except (tarfile.TarError, OSError) as e: + raise WorkspaceArchiveWriteError(path=archive_path, cause=e) from e + + async def extract_zip_archive( + self, + *, + archive_path: Path, + destination_root: Path, + data: io.IOBase, + ) -> None: + child_entry_cache: dict[Path, dict[str, EntryKind]] = {} + try: + with zipfile.ZipFile(zipfile_compatible_stream(data)) as archive: + for member in archive.infolist(): + rel_path = safe_zip_member_rel_path(member) + if rel_path is None: + continue + + await self._ensure_no_symlink_extract_parents( + destination_root=destination_root, + rel_path=rel_path, + member_name=member.filename, + error_type="zip", + child_entry_cache=child_entry_cache, + ) + dest = destination_root / rel_path + if member.is_dir(): + await self._mkdir(dest) + self._record_extract_entry( + child_entry_cache=child_entry_cache, + destination_root=destination_root, + path=dest, + kind=EntryKind.DIRECTORY, + ) + continue + + await self._mkdir(dest.parent) + self._record_extract_entry( + child_entry_cache=child_entry_cache, + destination_root=destination_root, + path=dest.parent, + kind=EntryKind.DIRECTORY, + ) + with archive.open(member, mode="r") as member_data: + await self._write(dest, cast(io.IOBase, member_data)) + self._record_extract_entry( + child_entry_cache=child_entry_cache, + destination_root=destination_root, + path=dest, + kind=EntryKind.FILE, + ) + except UnsafeZipMemberError as e: + raise WorkspaceArchiveWriteError( + path=archive_path, + context={"member": e.member, "reason": e.reason}, + cause=e, + ) from e + except ValueError as e: + raise WorkspaceArchiveWriteError(path=archive_path, cause=e) from e + except (zipfile.BadZipFile, OSError) as e: + raise WorkspaceArchiveWriteError(path=archive_path, cause=e) from e + + async def _ensure_no_symlink_extract_parents( + self, + *, + destination_root: Path, + rel_path: Path, + member_name: str, + error_type: Literal["tar", "zip"], + child_entry_cache: dict[Path, dict[str, EntryKind]], + ) -> None: + symlink_component = await self._find_symlink_component( + base_dir=destination_root, + rel_path=rel_path, + child_entry_cache=child_entry_cache, + ) + if symlink_component is None: + return + + reason = f"symlink in parent path: {symlink_component.as_posix()}" + if error_type == "tar": + raise UnsafeTarMemberError(member=member_name, reason=reason) + raise UnsafeZipMemberError(member=member_name, reason=reason) + + async def _find_symlink_component( + self, + *, + base_dir: Path, + rel_path: Path, + child_entry_cache: dict[Path, dict[str, EntryKind]], + ) -> Path | None: + current_dir = base_dir + traversed = Path() + + for part in rel_path.parts: + entry_kind = await self._lookup_child_entry_kind( + current_dir, + part, + child_entry_cache=child_entry_cache, + ) + if entry_kind is None: + return None + + traversed /= part + if entry_kind == EntryKind.SYMLINK: + return traversed + + current_dir = current_dir / part + + return None + + async def _lookup_child_entry_kind( + self, + parent_dir: Path, + child_name: str, + *, + child_entry_cache: dict[Path, dict[str, EntryKind]], + ) -> EntryKind | None: + cached_entries = child_entry_cache.get(parent_dir) + if cached_entries is None: + try: + entries = await self._ls(parent_dir) + except ExecNonZeroError: + return None + cached_entries = {Path(entry.path).name: entry.kind for entry in entries} + child_entry_cache[parent_dir] = cached_entries + + return cached_entries.get(child_name) + + @staticmethod + def _record_extract_entry( + *, + child_entry_cache: dict[Path, dict[str, EntryKind]], + destination_root: Path, + path: Path, + kind: EntryKind, + ) -> None: + try: + rel_path = path.relative_to(destination_root) + except ValueError: + return + + if not rel_path.parts: + return + + current_dir = destination_root + for index, part in enumerate(rel_path.parts): + child_kind = kind if index == len(rel_path.parts) - 1 else EntryKind.DIRECTORY + cached_entries = child_entry_cache.get(current_dir) + if cached_entries is not None: + cached_entries[part] = child_kind + current_dir = current_dir / part + + +def zipfile_compatible_stream(stream: io.IOBase) -> io.IOBase: + seekable = getattr(stream, "seekable", None) + if callable(seekable): + return stream + return _ZipFileStreamAdapter(stream) + + +def safe_zip_member_rel_path(member: zipfile.ZipInfo) -> Path | None: + if member.filename in ("", ".", "./"): + return None + + rel = PurePosixPath(member.filename) + if rel.is_absolute(): + raise UnsafeZipMemberError(member=member.filename, reason="absolute path") + if ".." in rel.parts: + raise UnsafeZipMemberError(member=member.filename, reason="parent traversal") + + mode = (member.external_attr >> 16) & 0o170000 + if mode == 0o120000: + raise UnsafeZipMemberError(member=member.filename, reason="link member not allowed") + + return Path(*rel.parts) + + +class _ZipFileStreamAdapter(io.IOBase): + def __init__(self, stream: io.IOBase) -> None: + self._stream = stream + + def seekable(self) -> bool: + return True + + def readable(self) -> bool: + return True + + def tell(self) -> int: + return int(self._stream.tell()) + + def seek(self, offset: int, whence: int = io.SEEK_SET) -> int: + return int(self._stream.seek(offset, whence)) + + def read(self, size: int = -1) -> bytes: + data = self._stream.read(size) + if isinstance(data, bytes): + return data + raise TypeError(f"expected bytes from wrapped stream, got {type(data).__name__}") + + def close(self) -> None: + return diff --git a/src/agents/sandbox/session/base_sandbox_session.py b/src/agents/sandbox/session/base_sandbox_session.py new file mode 100644 index 0000000000..839d4fb5e3 --- /dev/null +++ b/src/agents/sandbox/session/base_sandbox_session.py @@ -0,0 +1,442 @@ +import abc +import io +import shlex +import shutil +import tempfile +from collections.abc import Sequence +from pathlib import Path +from typing import Literal, cast + +from typing_extensions import Self + +from ..entries import BaseEntry, resolve_workspace_path +from ..errors import ( + ExecNonZeroError, + InvalidCompressionSchemeError, +) +from ..files import FileEntry +from ..materialization import MaterializationResult, MaterializedFile +from ..snapshot import NoopSnapshot +from ..types import ExecResult, User +from ..util.parse_utils import parse_ls_la +from .archive_extraction import ( + WorkspaceArchiveExtractor, + safe_zip_member_rel_path, + zipfile_compatible_stream, +) +from .dependencies import Dependencies +from .manifest_application import ManifestApplier +from .sandbox_session_state import SandboxSessionState + + +class BaseSandboxSession(abc.ABC): + state: SandboxSessionState + _dependencies: Dependencies | None = None + _dependencies_closed: bool = False + + async def start(self) -> None: + if await self.state.snapshot.restorable(): + # Ensure the snapshot is the single source of truth on resume. + await self._clear_workspace_root_on_resume() + await self.hydrate_workspace(await self.state.snapshot.restore()) + if self.should_provision_manifest_accounts_on_resume(): + await self.provision_manifest_accounts() + # Reapply only ephemeral manifest entries on resume so persisted workspace state wins + # for durable files while temporary scaffolding is rebuilt for the new process. + await self.apply_manifest(only_ephemeral=True) + else: + await self.apply_manifest() + + async def stop(self) -> None: + """ + Persist/snapshot the workspace. + + Note: `stop()` is intentionally persistence-only. Sandboxes that need to tear down + sandbox resources (Docker containers, remote sessions, etc.) should implement + `shutdown()` instead. + """ + if isinstance(self.state.snapshot, NoopSnapshot): + return + await self.state.snapshot.persist(await self.persist_workspace()) + + @abc.abstractmethod + async def shutdown(self) -> None: + """ + Tear down sandbox resources (best-effort). + + Default is a no-op. Sandbox-specific sessions (e.g. Docker) should override. + """ + + async def __aenter__(self) -> Self: + await self.start() + return self + + async def aclose(self) -> None: + """Run the session cleanup lifecycle outside of ``async with``. + + This performs the same session-owned cleanup as ``__aexit__()``: persist/snapshot the + workspace via ``stop()``, tear down session resources via ``shutdown()``, and close + session-scoped dependencies. If the session came from a sandbox client, call the client's + ``delete()`` separately for backend-specific deletion such as removing a Docker container + or deleting a temporary host workspace. + """ + try: + await self.stop() + await self.shutdown() + finally: + await self._aclose_dependencies() + + async def __aexit__( + self, + exc_type: type[BaseException] | None, + exc: BaseException | None, + tb: object | None, + ) -> None: + await self.aclose() + + @property + def dependencies(self) -> Dependencies: + dependencies = self._dependencies + if dependencies is None: + dependencies = Dependencies() + self._dependencies = dependencies + self._dependencies_closed = False + return dependencies + + def set_dependencies(self, dependencies: Dependencies | None) -> None: + if dependencies is None: + return + self._dependencies = dependencies + self._dependencies_closed = False + + async def _aclose_dependencies(self) -> None: + dependencies = self._dependencies + if dependencies is None or self._dependencies_closed: + return + self._dependencies_closed = True + await dependencies.aclose() + + async def exec( + self, + *command: str | Path, + timeout: float | None = None, + shell: bool | list[str] = True, + user: str | User | None = None, + ) -> ExecResult: + """Execute a command inside the session. + + :param command: Command and args (will be stringified). + :param timeout: Optional wall-clock timeout in seconds. + :param shell: Whether to run this command in a shell. If ``True`` is provided, + the command will be run prefixed by ``sh -lc``. A custom shell prefix may be used + by providing a list. + + :returns: An ``ExecResult`` containing stdout/stderr and exit code. + + :raises TimeoutError: If the sandbox cannot complete within `timeout`. + """ + + sanitized_command = self._prepare_exec_command(*command, shell=shell, user=user) + return await self._exec_internal(*sanitized_command, timeout=timeout) + + def _prepare_exec_command( + self, + *command: str | Path, + shell: bool | list[str], + user: str | User | None, + ) -> list[str]: + sanitized_command = [str(c) for c in command] + + if shell: + joined = ( + sanitized_command[0] + if len(sanitized_command) == 1 + else shlex.join(sanitized_command) + ) + if isinstance(shell, list): + sanitized_command = shell + [joined] + else: + sanitized_command = ["sh", "-lc", joined] + + if user: + if isinstance(user, User): + user = user.name + + assert isinstance(user, str) + + sanitized_command = ["sudo", "-u", user, "--"] + sanitized_command + + return sanitized_command + + @abc.abstractmethod + async def _exec_internal( + self, + *command: str | Path, + timeout: float | None = None, + ) -> ExecResult: ... + + @abc.abstractmethod + async def read(self, path: Path) -> io.IOBase: + """Read a file from the session's workspace. + + :param path: Absolute path in the container or path relative to the + workspace root. + :returns: A readable file-like object. + :raises: FileNotFoundError: If the path does not exist. + """ + + @abc.abstractmethod + async def write(self, path: Path, data: io.IOBase) -> None: + """Write a file into the session's workspace. + + :param path: Absolute path in the container or path relative to the + workspace root. + :param data: A file-like object positioned at the start of the payload. + """ + + @abc.abstractmethod + async def running(self) -> bool: + """ + :returns: whether the underlying sandbox is currently running. + """ + + @abc.abstractmethod + async def persist_workspace(self) -> io.IOBase: + """Serialize the session's workspace into a byte stream. + + :returns: A readable tar binary stream representing the full workspace. + """ + + @abc.abstractmethod + async def hydrate_workspace(self, data: io.IOBase) -> None: + """Populate the session's workspace from a serialized byte stream. + + :param data: A readable tar binary stream as produced by `persist_workspace`. + """ + + async def ls(self, path: Path | str) -> list[FileEntry]: + """List directory contents. + + :param path: Path to list. + :returns: A list of `FileEntry` objects. + """ + path = self.normalize_path(path) + + cmd = ("ls", "-la", "--", str(path)) + result = await self.exec(*cmd, shell=False) + if not result.ok(): + raise ExecNonZeroError(result, command=cmd) + + return parse_ls_la(result.stdout.decode("utf-8", errors="replace"), base=str(path)) + + async def rm(self, path: Path | str, *, recursive: bool = False) -> None: + """Remove a file or directory. + + :param path: Path to remove. + :param recursive: If true, remove directories recursively. + """ + path = self.normalize_path(path) + + cmd: list[str] = ["rm"] + if recursive: + cmd.append("-rf") + cmd.extend(["--", str(path)]) + + result = await self.exec(*cmd, shell=False) + if not result.ok(): + raise ExecNonZeroError(result, command=cmd) + + async def mkdir(self, path: Path | str, *, parents: bool = False) -> None: + """Create a directory. + + :param path: Directory to create on the remote. + :param parents: If true, create missing parents. + """ + path = self.normalize_path(path) + + cmd: list[str] = ["mkdir"] + if parents: + cmd.append("-p") + cmd.append(str(path)) + + result = await self.exec(*cmd, shell=False) + if not result.ok(): + raise ExecNonZeroError(result, command=cmd) + + async def extract( + self, + path: Path | str, + data: io.IOBase, + *, + compression_scheme: Literal["tar", "zip"] | None = None, + ) -> None: + """ + Write a compressed archive to a destination on the remote. + Optionally extract the archive once written. + + :param path: Path on the host machine to extract to + :param data: a file-like io stream. + :param compression_scheme: either "tar" or "zip". If not provided, + it will try to infer from the path. + """ + if isinstance(path, str): + path = Path(path) + + if compression_scheme is None: + suffix = path.suffix.removeprefix(".") + compression_scheme = cast(Literal["tar", "zip"], suffix) if suffix else None + + if compression_scheme is None or compression_scheme not in ["zip", "tar"]: + raise InvalidCompressionSchemeError(path=path, scheme=compression_scheme) + + normalized_path = self.normalize_path(path) + destination_root = normalized_path.parent + + # Materialize the archive into a local spool once because both `write()` and the + # extraction step consume the stream, and zip extraction may require seeking. + spool = tempfile.SpooledTemporaryFile(max_size=16 * 1024 * 1024, mode="w+b") + try: + shutil.copyfileobj(data, spool) + spool.seek(0) + await self.write(normalized_path, spool) + spool.seek(0) + + if compression_scheme == "tar": + await self._extract_tar_archive( + archive_path=normalized_path, + destination_root=destination_root, + data=spool, + ) + else: + await self._extract_zip_archive( + archive_path=normalized_path, + destination_root=destination_root, + data=spool, + ) + finally: + spool.close() + + def normalize_path(self, path: Path | str) -> Path: + if isinstance(path, str): + path = Path(path) + + root = Path(self.state.manifest.root) + return resolve_workspace_path(root, path, allow_absolute_within_root=True) + + def describe(self) -> str: + return self.state.manifest.describe() + + async def _extract_tar_archive( + self, + *, + archive_path: Path, + destination_root: Path, + data: io.IOBase, + ) -> None: + extractor = WorkspaceArchiveExtractor( + mkdir=lambda path: self.mkdir(path, parents=True), + write=self.write, + ls=lambda path: self.ls(path), + ) + await extractor.extract_tar_archive( + archive_path=archive_path, + destination_root=destination_root, + data=data, + ) + + async def _extract_zip_archive( + self, + *, + archive_path: Path, + destination_root: Path, + data: io.IOBase, + ) -> None: + extractor = WorkspaceArchiveExtractor( + mkdir=lambda path: self.mkdir(path, parents=True), + write=self.write, + ls=lambda path: self.ls(path), + ) + await extractor.extract_zip_archive( + archive_path=archive_path, + destination_root=destination_root, + data=data, + ) + + @staticmethod + def _zipfile_compatible_stream(stream: io.IOBase) -> io.IOBase: + return zipfile_compatible_stream(stream) + + @staticmethod + def _safe_zip_member_rel_path(member) -> Path | None: + return safe_zip_member_rel_path(member) + + async def apply_manifest(self, *, only_ephemeral: bool = False) -> MaterializationResult: + applier = ManifestApplier( + mkdir=lambda path: self.mkdir(path, parents=True), + exec_checked_nonzero=self._exec_checked_nonzero, + apply_entry=lambda artifact, dest, base_dir: artifact.apply(self, dest, base_dir), + ) + return await applier.apply_manifest( + self.state.manifest, + only_ephemeral=only_ephemeral, + base_dir=self._manifest_base_dir(), + ) + + async def provision_manifest_accounts(self) -> None: + applier = ManifestApplier( + mkdir=lambda path: self.mkdir(path, parents=True), + exec_checked_nonzero=self._exec_checked_nonzero, + apply_entry=lambda artifact, dest, base_dir: artifact.apply(self, dest, base_dir), + ) + await applier.provision_accounts(self.state.manifest) + + def should_provision_manifest_accounts_on_resume(self) -> bool: + return True + + async def _apply_entry_batch( + self, + entries: Sequence[tuple[Path, BaseEntry]], + *, + base_dir: Path, + ) -> list[MaterializedFile]: + applier = ManifestApplier( + mkdir=lambda path: self.mkdir(path, parents=True), + exec_checked_nonzero=self._exec_checked_nonzero, + apply_entry=lambda artifact, dest, current_base_dir: artifact.apply( + self, + dest, + current_base_dir, + ), + ) + return await applier._apply_entry_batch(entries, base_dir=base_dir) + + def _manifest_base_dir(self) -> Path: + return Path.cwd() + + async def _exec_checked_nonzero(self, *command: str | Path) -> ExecResult: + result = await self.exec(*command, shell=False) + if not result.ok(): + raise ExecNonZeroError(result, command=command) + return result + + async def _clear_workspace_root_on_resume(self) -> None: + """ + Best-effort cleanup step for snapshot resume. + + We intentionally clear *contents* of the workspace root rather than deleting the root + directory itself. Some sandboxes configure their process working directory to the workspace + root (e.g. Modal sandboxes), and deleting the directory can make subsequent exec() calls + fail with "failed to find initial working directory". + """ + + root = Path(self.state.manifest.root) + try: + entries = await self.ls(root) + except ExecNonZeroError: + # If the root doesn't exist (or isn't listable), treat it as empty and let hydrate/apply + # create it as needed. + return + + for entry in entries: + # `parse_ls_la` filters "." and ".." already; remove everything else recursively. + await self.rm(Path(entry.path), recursive=True) diff --git a/src/agents/sandbox/session/dependencies.py b/src/agents/sandbox/session/dependencies.py new file mode 100644 index 0000000000..cb1cec7552 --- /dev/null +++ b/src/agents/sandbox/session/dependencies.py @@ -0,0 +1,201 @@ +from __future__ import annotations + +import inspect +from collections.abc import Awaitable, Callable, Mapping +from dataclasses import dataclass +from typing import cast + +from typing_extensions import Self + +DependencyKey = str + + +class DependenciesError(RuntimeError): + pass + + +class DependenciesBindingError(DependenciesError, ValueError): + pass + + +class DependenciesMissingDependencyError(DependenciesError, LookupError): + pass + + +FactoryFn = Callable[["Dependencies"], object | Awaitable[object]] + + +@dataclass(slots=True) +class _ValueBinding: + value: object + + +@dataclass(slots=True) +class _FactoryBinding: + factory: FactoryFn + cache: bool + owns_result: bool + + +_Binding = _ValueBinding | _FactoryBinding + + +async def _close_best_effort(value: object) -> None: + close = getattr(value, "aclose", None) + if close is not None: + try: + result = close() + if inspect.isawaitable(result): + await cast(Awaitable[object], result) + return + except Exception: + return + + close = getattr(value, "close", None) + if close is None: + return + try: + result = close() + if inspect.isawaitable(result): + await cast(Awaitable[object], result) + except Exception: + return + + +class Dependencies: + """Session-scoped dependency container for manifest entry materialization. + + Sandbox clients hold a configured template of bindings and clone it for each created or resumed + session. That gives each session its own cache and owned-resource lifecycle while still letting + callers register shared runtime-only objects such as service clients or lazy factories. + """ + + def __init__(self) -> None: + self._bindings: dict[DependencyKey, _Binding] = {} + self._cache: dict[DependencyKey, object] = {} + self._owned_results: list[object] = [] + self._closed = False + + @classmethod + def with_values( + cls, + values: Mapping[DependencyKey, object], + ) -> Dependencies: + dependencies = cls() + for key, value in values.items(): + dependencies.bind_value(key, value) + return dependencies + + def bind_value( + self, + key: DependencyKey, + value: object, + *, + overwrite: bool = False, + ) -> Self: + if not key: + raise ValueError("Dependency key must be non-empty") + self._bind(key, _ValueBinding(value=value), overwrite=overwrite) + return self + + def clone(self) -> Dependencies: + cloned = Dependencies() + for key, binding in self._bindings.items(): + if isinstance(binding, _ValueBinding): + cloned._bindings[key] = _ValueBinding(value=binding.value) + else: + cloned._bindings[key] = _FactoryBinding( + factory=binding.factory, + cache=binding.cache, + owns_result=binding.owns_result, + ) + return cloned + + def bind_factory( + self, + key: DependencyKey, + factory: FactoryFn, + *, + cache: bool = True, + overwrite: bool = False, + owns_result: bool = False, + ) -> Self: + if not key: + raise ValueError("Dependency key must be non-empty") + self._bind( + key, + _FactoryBinding( + factory=factory, + cache=cache, + owns_result=owns_result, + ), + overwrite=overwrite, + ) + return self + + def _bind( + self, + key: DependencyKey, + binding: _Binding, + *, + overwrite: bool, + ) -> None: + if not overwrite and key in self._bindings: + raise DependenciesBindingError(f"Dependency `{key}` is already bound") + self._bindings[key] = binding + self._cache.pop(key, None) + + async def get(self, key: DependencyKey) -> object | None: + binding = self._bindings.get(key) + if binding is None: + return None + return await self._resolve(key, binding) + + async def require( + self, + key: DependencyKey, + *, + consumer: str | None = None, + ) -> object: + value = await self.get(key) + if value is not None: + return value + + consumer_part = f" for {consumer}" if consumer else "" + raise DependenciesMissingDependencyError( + f"Missing dependency `{key}`{consumer_part}. " + "Bind it on a Dependencies instance and pass it as " + "`dependencies=` when constructing the sandbox client." + ) + + async def _resolve(self, key: DependencyKey, binding: _Binding) -> object: + if isinstance(binding, _ValueBinding): + return binding.value + + assert isinstance(binding, _FactoryBinding) + if binding.cache and key in self._cache: + return self._cache[key] + + produced = binding.factory(self) + value = ( + await cast(Awaitable[object], produced) if inspect.isawaitable(produced) else produced + ) + + if binding.cache: + self._cache[key] = value + if binding.owns_result: + self._owned_results.append(value) + return value + + async def aclose(self) -> None: + if self._closed: + return + self._closed = True + + seen_ids: set[int] = set() + for value in reversed(self._owned_results): + value_id = id(value) + if value_id in seen_ids: + continue + seen_ids.add(value_id) + await _close_best_effort(value) diff --git a/src/agents/sandbox/session/events.py b/src/agents/sandbox/session/events.py new file mode 100644 index 0000000000..3d9e3145d7 --- /dev/null +++ b/src/agents/sandbox/session/events.py @@ -0,0 +1,89 @@ +from __future__ import annotations + +import uuid +from datetime import datetime, timezone +from typing import Annotated, Literal + +from pydantic import BaseModel, Field, TypeAdapter + +from ..errors import ErrorCode, OpName + +EventPhase = Literal["start", "finish"] + + +def _utcnow() -> datetime: + return datetime.now(tz=timezone.utc) + + +class EventPayloadPolicy(BaseModel): + """Controls how much potentially sensitive/large data is included in events.""" + + # Exec output can be noisy and sensitive; default off. + include_exec_output: bool = Field(default=False) + + # When enabled, bound output sizes. + max_stdout_chars: int = Field(default=8_000, ge=0) + max_stderr_chars: int = Field(default=8_000, ge=0) + + # For write events, we only include a best-effort byte count (never file bytes). + include_write_len: bool = Field(default=True) + + +class UCEventBase(BaseModel): + """Shared fields for all instrumentation events.""" + + version: int = Field(default=1) + + event_id: uuid.UUID = Field(default_factory=uuid.uuid4) + ts: datetime = Field(default_factory=_utcnow) + + session_id: uuid.UUID + seq: int + + op: OpName + phase: EventPhase + + span_id: uuid.UUID + parent_span_id: uuid.UUID | None = None + + # Operation-specific metadata (paths, argv, timings, etc.) + data: dict[str, object] = Field(default_factory=dict) + + +class UCStartEvent(UCEventBase): + """The start event for an operation.""" + + phase: Literal["start"] = Field(default="start") + + +class UCFinishEvent(UCEventBase): + """The finish event for an operation.""" + + phase: Literal["finish"] = Field(default="finish") + + ok: bool + duration_ms: float + + error_code: ErrorCode | None = None + error_type: str | None = None + error_message: str | None = None + + # Optional exec outputs (truncated / opt-in via policy). + stdout: str | None = None + stderr: str | None = None + + # Raw exec outputs (bytes) for per-sink/per-op policy application. + # These are excluded from serialization (JSONL / HTTP) by default. + stdout_bytes: bytes | None = Field(default=None, exclude=True) + stderr_bytes: bytes | None = Field(default=None, exclude=True) + + +# Discriminated union keyed by `phase`. +UCEvent = Annotated[UCStartEvent | UCFinishEvent, Field(discriminator="phase")] +_UC_EVENT_ADAPTER: TypeAdapter[UCEvent] = TypeAdapter(UCEvent) + + +def validate_uc_event(obj: object) -> UCEvent: + """Parse an event payload (e.g. from JSON) into the correct phase-specific model.""" + + return _UC_EVENT_ADAPTER.validate_python(obj) diff --git a/src/agents/sandbox/session/manager.py b/src/agents/sandbox/session/manager.py new file mode 100644 index 0000000000..91a6dbf025 --- /dev/null +++ b/src/agents/sandbox/session/manager.py @@ -0,0 +1,159 @@ +from __future__ import annotations + +import asyncio +import logging +from collections.abc import Sequence + +from ..errors import OpName +from .events import EventPayloadPolicy, UCEvent, UCFinishEvent +from .sinks import ChainedSink, EventSink +from .utils import _safe_decode + +logger = logging.getLogger(__name__) + + +class Instrumentation: + def __init__( + self, + *, + sinks: Sequence[EventSink] | None = None, + payload_policy: EventPayloadPolicy | None = None, + payload_policy_by_op: dict[OpName, EventPayloadPolicy] | None = None, + ) -> None: + self._sinks: list[EventSink] = list(sinks or []) + self.payload_policy = payload_policy or EventPayloadPolicy() + self.payload_policy_by_op = payload_policy_by_op or {} + self._tasks: set[asyncio.Task[None]] = set() + + @property + def sinks(self) -> list[EventSink]: + return list(self._sinks) + + def add_sink(self, sink: EventSink) -> None: + self._sinks.append(sink) + + async def emit(self, event: UCEvent) -> None: + for sink in self._sinks: + if isinstance(sink, ChainedSink): + for inner in sink.sinks: + policy = self._policy_for(event.op, inner) + per_sink_event = self._apply_policy(event, policy) + # ChainedSink promises in-order delivery; ensure each sink completes + # before moving on, regardless of inner sink.mode. + await self._deliver_chained(inner, per_sink_event) + else: + policy = self._policy_for(event.op, sink) + per_sink_event = self._apply_policy(event, policy) + await self._deliver(sink, per_sink_event) + + async def flush(self) -> None: + pending = tuple(self._tasks) + if not pending: + return + await asyncio.gather(*pending, return_exceptions=True) + + def _policy_for(self, op: OpName, sink: EventSink) -> EventPayloadPolicy: + # Merge semantics: default -> per-op overrides -> per-sink overrides. + effective = self.payload_policy.model_copy(deep=True) + + op_policy = self.payload_policy_by_op.get(op) + if op_policy is not None: + effective = effective.model_copy(update=self._overrides(op_policy)) + + sink_policy = getattr(sink, "payload_policy", None) + if sink_policy is not None: + effective = effective.model_copy(update=self._overrides(sink_policy)) + + return effective + + def _overrides(self, policy: EventPayloadPolicy) -> dict[str, object]: + # Only override fields explicitly set by the user. + return {name: getattr(policy, name) for name in policy.model_fields_set} + + def _apply_policy(self, event: UCEvent, policy: EventPayloadPolicy) -> UCEvent: + # Clone per sink so we can redact/augment fields without affecting other sinks. + out = event.model_copy(deep=True) + + # Generic stream-length metadata redaction. + if not policy.include_write_len and "bytes" in out.data: + out.data.pop("bytes", None) + + # Exec output redaction/formatting. + if isinstance(out, UCFinishEvent): + if not policy.include_exec_output: + out.stdout = None + out.stderr = None + out.stdout_bytes = None + out.stderr_bytes = None + else: + if out.stdout_bytes is not None: + out.stdout = _safe_decode(out.stdout_bytes, max_chars=policy.max_stdout_chars) + if out.stderr_bytes is not None: + out.stderr = _safe_decode(out.stderr_bytes, max_chars=policy.max_stderr_chars) + + return out + + async def _deliver(self, sink: EventSink, event: UCEvent) -> None: + async def _run() -> None: + await sink.handle(event) + + if sink.mode == "sync": + try: + await _run() + except Exception: + self._handle_sink_error(sink, event) + elif sink.mode == "async": + if sink.on_error == "raise": + await _run() + return + + async def _task() -> None: + try: + await _run() + except Exception: + self._handle_sink_error(sink, event) + + task = asyncio.create_task(_task()) + # Track background deliveries so the task is kept alive and can be discarded once done. + self._tasks.add(task) + task.add_done_callback(self._tasks.discard) + elif sink.mode == "best_effort": + + async def _task() -> None: + try: + await _run() + except Exception: + self._handle_sink_error(sink, event, force_no_raise=True) + + task = asyncio.create_task(_task()) + # Same bookkeeping as async mode, but failures are always swallowed after logging. + self._tasks.add(task) + task.add_done_callback(self._tasks.discard) + else: + raise AssertionError(f"unknown sink.mode: {sink.mode!r}") + + async def _deliver_chained(self, sink: EventSink, event: UCEvent) -> None: + """ + Deliver an event to a sink as part of a ChainedSink group. + + The ChainedSink contract is "run in order", which implies later sinks should not + observe side effects before earlier sinks complete. To uphold that, we always + await completion here (ignoring sink.mode scheduling). + """ + try: + await sink.handle(event) + except Exception: + force_no_raise = sink.mode == "best_effort" + self._handle_sink_error(sink, event, force_no_raise=force_no_raise) + + def _handle_sink_error( + self, sink: EventSink, event: UCEvent, *, force_no_raise: bool = False + ) -> None: + if force_no_raise or sink.on_error in ("log", "ignore"): + if sink.on_error == "log": + logger.exception("instrumentation sink failed (ignored): %s", type(sink).__name__) + return + raise RuntimeError( + "instrumentation sink failed: " + f"{type(sink).__name__} while handling event {event.event_id}" + ) diff --git a/src/agents/sandbox/session/manifest_application.py b/src/agents/sandbox/session/manifest_application.py new file mode 100644 index 0000000000..039713f15c --- /dev/null +++ b/src/agents/sandbox/session/manifest_application.py @@ -0,0 +1,169 @@ +from __future__ import annotations + +from collections.abc import Awaitable, Callable, Sequence +from pathlib import Path + +from ..entries import BaseEntry, Dir, Mount, resolve_workspace_path +from ..manifest import Manifest +from ..materialization import MaterializationResult, MaterializedFile, gather_in_order +from ..types import ExecResult, User + + +class ManifestApplier: + def __init__( + self, + *, + mkdir: Callable[[Path], Awaitable[None]], + exec_checked_nonzero: Callable[..., Awaitable[ExecResult]], + apply_entry: Callable[[BaseEntry, Path, Path], Awaitable[list[MaterializedFile]]], + ) -> None: + self._mkdir = mkdir + self._exec_checked_nonzero = exec_checked_nonzero + self._apply_entry = apply_entry + + async def apply_manifest( + self, + manifest: Manifest, + *, + only_ephemeral: bool = False, + base_dir: Path | None = None, + ) -> MaterializationResult: + base_dir = Path("/") if base_dir is None else base_dir + + await self._mkdir(Path(manifest.root)) + + if not only_ephemeral: + await self.provision_accounts(manifest) + + entries_to_apply: list[tuple[Path, BaseEntry]] = [] + if only_ephemeral: + for rel_dest, artifact in self._ephemeral_entries(manifest): + dest = resolve_workspace_path(Path(manifest.root), rel_dest) + entries_to_apply.append((dest, artifact)) + else: + for raw_rel_dest, artifact in manifest.validated_entries().items(): + dest = resolve_workspace_path( + Path(manifest.root), + Manifest._coerce_rel_path(raw_rel_dest), + ) + entries_to_apply.append((dest, artifact)) + + return MaterializationResult( + files=await self._apply_entry_batch(entries_to_apply, base_dir=base_dir), + ) + + async def provision_accounts(self, manifest: Manifest) -> None: + all_users: set[User] = set(manifest.users) + for group in manifest.groups: + all_users |= set(group.users) + await self._exec_checked_nonzero("groupadd", group.name) + + for user in all_users: + await self._exec_checked_nonzero( + "useradd", + "-U", + "-M", + "-s", + "/usr/sbin/nologin", + user.name, + ) + + for group in manifest.groups: + for user in group.users: + await self._exec_checked_nonzero("usermod", "-aG", group.name, user.name) + + def _ephemeral_entries(self, manifest: Manifest) -> list[tuple[Path, BaseEntry]]: + entries: list[tuple[Path, BaseEntry]] = [] + for rel_dest, artifact in manifest.entries.items(): + self._collect_ephemeral_entries( + rel_dest=Manifest._coerce_rel_path(rel_dest), + artifact=artifact, + out=entries, + ) + return entries + + def _collect_ephemeral_entries( + self, + *, + rel_dest: Path, + artifact: BaseEntry, + out: list[tuple[Path, BaseEntry]], + ) -> None: + manifest_rel = Manifest._coerce_rel_path(rel_dest) + Manifest._validate_rel_path(manifest_rel) + if artifact.ephemeral: + out.append((manifest_rel, self._prune_to_ephemeral(artifact))) + return + if isinstance(artifact, Dir): + for child_name, child_artifact in artifact.children.items(): + self._collect_ephemeral_entries( + rel_dest=manifest_rel / Manifest._coerce_rel_path(child_name), + artifact=child_artifact, + out=out, + ) + + def _prune_to_ephemeral(self, artifact: BaseEntry) -> BaseEntry: + if not isinstance(artifact, Dir): + return artifact + if artifact.ephemeral: + return artifact.model_copy(deep=True) + + pruned_children: dict[str | Path, BaseEntry] = {} + for child_name, child_artifact in artifact.children.items(): + if child_artifact.ephemeral: + pruned_children[child_name] = self._prune_to_ephemeral(child_artifact) + continue + if isinstance(child_artifact, Dir): + nested = self._prune_to_ephemeral(child_artifact) + if isinstance(nested, Dir) and nested.children: + pruned_children[child_name] = nested + + return artifact.model_copy(update={"children": pruned_children}, deep=True) + + @staticmethod + def _paths_overlap(left: Path, right: Path) -> bool: + return left == right or left in right.parents or right in left.parents + + async def _apply_entry_batch( + self, + entries: Sequence[tuple[Path, BaseEntry]], + *, + base_dir: Path, + ) -> list[MaterializedFile]: + files: list[MaterializedFile] = [] + parallel_batch: list[tuple[Path, BaseEntry]] = [] + + async def _flush_parallel_batch() -> None: + nonlocal files + if not parallel_batch: + return + + def _make_apply_task( + dest: Path, + artifact: BaseEntry, + ) -> Callable[[], Awaitable[list[MaterializedFile]]]: + async def _apply() -> list[MaterializedFile]: + return await self._apply_entry(artifact, dest, base_dir) + + return _apply + + batch = list(parallel_batch) + parallel_batch.clear() + batch_files = await gather_in_order( + [_make_apply_task(dest, artifact) for dest, artifact in batch] + ) + for entry_files in batch_files: + files.extend(entry_files) + + for dest, artifact in entries: + if isinstance(artifact, Mount) or any( + self._paths_overlap(dest, queued_dest) for queued_dest, _ in parallel_batch + ): + await _flush_parallel_batch() + files.extend(await self._apply_entry(artifact, dest, base_dir)) + continue + + parallel_batch.append((dest, artifact)) + + await _flush_parallel_batch() + return files diff --git a/src/agents/sandbox/session/sandbox_client.py b/src/agents/sandbox/session/sandbox_client.py new file mode 100644 index 0000000000..3fe84b13b3 --- /dev/null +++ b/src/agents/sandbox/session/sandbox_client.py @@ -0,0 +1,86 @@ +from __future__ import annotations + +import abc +from typing import Generic, TypeVar + +from ..manifest import Manifest +from ..snapshot import SnapshotSpec +from .base_sandbox_session import BaseSandboxSession +from .dependencies import Dependencies +from .manager import Instrumentation +from .sandbox_session import SandboxSession +from .sandbox_session_state import SandboxSessionState + +ClientOptionsT = TypeVar("ClientOptionsT") + + +class BaseSandboxClient(abc.ABC, Generic[ClientOptionsT]): + backend_id: str + supports_default_options: bool = False + _dependencies: Dependencies | None = None + + def _resolve_dependencies(self) -> Dependencies | None: + if self._dependencies is None: + return None + # Sessions get clones instead of the shared template so per-session factory caches and + # owned resources do not leak across unrelated sandboxes. + return self._dependencies.clone() + + def _wrap_session( + self, + inner: BaseSandboxSession, + *, + instrumentation: Instrumentation | None = None, + ) -> SandboxSession: + # Always return the instrumented wrapper so callers get consistent events and dependency + # lifecycle handling regardless of which backend created the inner session. + return SandboxSession( + inner, + instrumentation=instrumentation, + dependencies=self._resolve_dependencies(), + ) + + @abc.abstractmethod + async def create( + self, + *, + snapshot: SnapshotSpec | None = None, + manifest: Manifest | None = None, + options: ClientOptionsT, + ) -> SandboxSession: + """Create a new session. + + Args: + snapshot: Snapshot spec used to create a snapshot instance for + the session. If omitted, the session uses a no-op snapshot. + manifest: Optional manifest to materialize into the workspace when + the session starts. + options: Sandbox-specific settings. For example, Docker expects + ``DockerSandboxClientOptions(image="...")``. + Returns: + A `SandboxSession` that can be entered with `async with` or closed explicitly with + `await session.aclose()`. + """ + + @abc.abstractmethod + async def delete(self, session: SandboxSession) -> SandboxSession: + """Delete a session and release sandbox resources.""" + + @abc.abstractmethod + async def resume( + self, + state: SandboxSessionState, + ) -> SandboxSession: + """Resume a session from a previously persisted `SandboxSessionState`. + + The returned session should hydrate its workspace from `state.snapshot` + during `SandboxSession.start()`. + """ + + def serialize_session_state(self, state: SandboxSessionState) -> dict[str, object]: + """Serialize backend-specific sandbox state into a JSON-compatible payload.""" + return state.model_dump(mode="json") + + @abc.abstractmethod + def deserialize_session_state(self, payload: dict[str, object]) -> SandboxSessionState: + """Deserialize backend-specific sandbox state from a JSON-compatible payload.""" diff --git a/src/agents/sandbox/session/sandbox_session.py b/src/agents/sandbox/session/sandbox_session.py new file mode 100644 index 0000000000..4e30967242 --- /dev/null +++ b/src/agents/sandbox/session/sandbox_session.py @@ -0,0 +1,398 @@ +from __future__ import annotations + +import io +import time +import uuid +from collections.abc import Coroutine +from contextvars import Token +from functools import wraps +from pathlib import Path +from typing import Callable, TypeVar, cast + +from ..errors import OpName, UniversalComputerError +from ..types import ExecResult, User +from .base_sandbox_session import BaseSandboxSession +from .dependencies import Dependencies +from .events import UCFinishEvent, UCStartEvent +from .manager import Instrumentation +from .sandbox_session_state import SandboxSessionState +from .sinks import ChainedSink, SandboxSessionBoundSink +from .utils import ( + _best_effort_stream_len, + current_span_id, +) + +T = TypeVar("T") +F = TypeVar("F", bound=Callable[..., Coroutine[object, object, object]]) + + +def instrumented_op( + op: OpName, + *, + data: Callable[..., dict[str, object] | None] | None = None, + finish_data: ( + Callable[[dict[str, object] | None, object], dict[str, object] | None] | None + ) = None, + ok: Callable[[object], bool] | None = None, + outputs: Callable[[object], tuple[bytes | None, bytes | None]] | None = None, +) -> Callable[[F], F]: + """Decorator to emit UCEvents around a SandboxSession operation.""" + + def _decorator(fn: F) -> F: + @wraps(fn) + async def _wrapped(self: SandboxSession, *args: object, **kwargs: object) -> object: + start_data = data(self, *args, **kwargs) if data is not None else None + finish_cb: Callable[[object], dict[str, object]] | None + if finish_data is None: + finish_cb = None + else: + fd = finish_data + + def _finish_cb(res: object) -> dict[str, object]: + return dict(fd(start_data, res) or {}) + + finish_cb = _finish_cb + + return await self._annotate( + op=op, + start_data=start_data, + run=lambda: fn(self, *args, **kwargs), + finish_data=finish_cb, + ok=ok, + outputs=outputs, + ) + + return cast(F, _wrapped) + + return _decorator + + +def _exec_start_data( + _self: SandboxSession, + *command: str | Path, + timeout: float | None = None, + shell: bool | list[str] = True, + user: str | User | None = None, +) -> dict[str, object]: + user_value: str | None + if isinstance(user, User): + user_value = user.name + else: + user_value = user + return { + "command": [str(c) for c in command], + "timeout_s": timeout, + "shell": shell, + "user": user_value, + } + + +def _exec_finish_data(start_data: dict[str, object] | None, result: object) -> dict[str, object]: + out = dict(start_data or {}) + out["exit_code"] = cast(ExecResult, result).exit_code + return out + + +def _write_start_data(self: SandboxSession, path: Path, data: io.IOBase) -> dict[str, object]: + out: dict[str, object] = {"path": str(path)} + n = _best_effort_stream_len(data) + if n is not None: + out["bytes"] = n + return out + + +def _running_finish_data( + _start_data: dict[str, object] | None, + result: object, +) -> dict[str, object]: + return {"alive": bool(result)} + + +def _snapshot_tar_path(self: SandboxSession) -> str | None: + """ + Best-effort path to the persisted workspace tar on the *host*. + + Today Snapshot is a LocalSnapshot whose persist() writes `/.tar`. + We keep this best-effort (instead of importing LocalSnapshot) to avoid coupling. + """ + + snap = getattr(self.state, "snapshot", None) + base_path = getattr(snap, "base_path", None) + snap_id = getattr(snap, "id", None) + if isinstance(base_path, Path) and isinstance(snap_id, str) and snap_id: + return str(Path(str(base_path / snap_id) + ".tar")) + return None + + +def _persist_start_data(self: SandboxSession) -> dict[str, object]: + out: dict[str, object] = {"workspace_root": str(self.state.manifest.root)} + tar_path = _snapshot_tar_path(self) + if tar_path is not None: + out["tar_path"] = tar_path + return out + + +def _persist_finish_data( + start_data: dict[str, object] | None, + result: object, +) -> dict[str, object]: + out = dict(start_data or {}) + n = _best_effort_stream_len(cast(io.IOBase, result)) + if n is not None: + out["bytes"] = n + return out + + +def _hydrate_start_data(self: SandboxSession, data: io.IOBase) -> dict[str, object]: + out: dict[str, object] = {"untar_dir": str(self.state.manifest.root)} + n = _best_effort_stream_len(data) + if n is not None: + out["bytes"] = n + return out + + +class SandboxSession(BaseSandboxSession): + """A SandboxSession wrapper that emits UCEvent objects around core operations.""" + + _inner: BaseSandboxSession + _instrumentation: Instrumentation + _seq: int + + def __init__( + self, + inner: BaseSandboxSession, + *, + instrumentation: Instrumentation | None = None, + dependencies: Dependencies | None = None, + ) -> None: + self._inner = inner + self._inner.set_dependencies(dependencies) + self._instrumentation = instrumentation or Instrumentation() + self._seq = 0 + + self._bind_session_to_sinks() + + def _bind_session_to_sinks(self) -> None: + # Bind sinks to the *inner* session to avoid recursive instrumentation loops. + for sink in self._instrumentation.sinks: + sinks: list[object] + if isinstance(sink, ChainedSink): + sinks = list(sink.sinks) + else: + sinks = [sink] + for s in sinks: + if isinstance(s, SandboxSessionBoundSink): + s.bind(self._inner) + + @property + def state(self) -> SandboxSessionState: + return self._inner.state + + @state.setter + def state(self, value: SandboxSessionState) -> None: # pragma: no cover + self._inner.state = value + + @property + def dependencies(self) -> Dependencies: + return self._inner.dependencies + + async def _aclose_dependencies(self) -> None: + await self._inner._aclose_dependencies() + + async def aclose(self) -> None: + try: + await super().aclose() + finally: + await self._instrumentation.flush() + + def _next_seq(self) -> int: + self._seq += 1 + return self._seq + + async def _emit_start_event( + self, + *, + op: OpName, + span_id: uuid.UUID, + parent_span_id: uuid.UUID | None, + data: dict[str, object] | None = None, + ) -> None: + await self._instrumentation.emit( + UCStartEvent( + session_id=self.state.session_id, + seq=self._next_seq(), + op=op, + span_id=span_id, + parent_span_id=parent_span_id, + data=data or {}, + ) + ) + + async def _annotate( + self, + *, + op: OpName, + start_data: dict[str, object] | None, + run: Callable[[], Coroutine[object, object, T]], + finish_data: Callable[[T], dict[str, object]] | None = None, + ok: Callable[[T], bool] | None = None, + outputs: Callable[[T], tuple[bytes | None, bytes | None]] | None = None, + ) -> T: + span_id = uuid.uuid4() + parent = current_span_id.get() + token = current_span_id.set(span_id) + + try: + await self._emit_start_event( + op=op, span_id=span_id, parent_span_id=parent, data=start_data + ) + except Exception: + current_span_id.reset(token) + raise + + t0 = time.monotonic() + try: + value = await run() + except Exception as e: + await self._emit_finish_event( + op=op, + span_id=span_id, + parent_span_id=parent, + start_t=t0, + token=token, + ok=False, + exc=e, + data=start_data, + stdout=None, + stderr=None, + ) + raise + + data_finish = finish_data(value) if finish_data is not None else start_data + ok_value = ok(value) if ok is not None else True + stdout, stderr = outputs(value) if outputs is not None else (None, None) + await self._emit_finish_event( + op=op, + span_id=span_id, + parent_span_id=parent, + start_t=t0, + token=token, + ok=ok_value, + exc=None, + data=data_finish, + stdout=stdout, + stderr=stderr, + ) + return value + + async def _emit_finish_event( + self, + *, + op: OpName, + span_id: uuid.UUID, + parent_span_id: uuid.UUID | None, + start_t: float, + token: Token[uuid.UUID | None], + ok: bool, + exc: BaseException | None, + data: dict[str, object] | None, + stdout: bytes | None, + stderr: bytes | None, + ) -> None: + duration_ms = (time.monotonic() - start_t) * 1000.0 + event = UCFinishEvent( + session_id=self.state.session_id, + seq=self._next_seq(), + op=op, + span_id=span_id, + parent_span_id=parent_span_id, + data=data or {}, + ok=ok, + duration_ms=duration_ms, + ) + + if exc is not None: + event.error_type = type(exc).__name__ + event.error_message = str(exc) + if isinstance(exc, UniversalComputerError): + event.error_code = exc.error_code + + # Preserve raw bytes so Instrumentation can apply per-op/per-sink policies later. + # Decoding here would force one global formatting decision before sink-specific redaction + # and truncation rules have a chance to run. + event.stdout_bytes = stdout + event.stderr_bytes = stderr + + try: + await self._instrumentation.emit(event) + finally: + current_span_id.reset(token) + + @instrumented_op("start") + async def start(self) -> None: + await self._inner.start() + + @instrumented_op("stop") + async def stop(self) -> None: + await self._inner.stop() + + @instrumented_op("shutdown") + async def shutdown(self) -> None: + await self._inner.shutdown() + + @instrumented_op( + "exec", + data=_exec_start_data, + finish_data=_exec_finish_data, + ok=lambda result: cast(ExecResult, result).ok(), + outputs=lambda result: ( + cast(ExecResult, result).stdout, + cast(ExecResult, result).stderr, + ), + ) + async def exec( + self, + *command: str | Path, + timeout: float | None = None, + shell: bool | list[str] = True, + user: str | User | None = None, + ) -> ExecResult: + return await self._inner.exec(*command, timeout=timeout, shell=shell, user=user) + + async def _exec_internal( + self, + *command: str | Path, + timeout: float | None = None, + ) -> ExecResult: + raise NotImplementedError("this should never be invoked") + + @instrumented_op("read", data=lambda _self, path: {"path": str(path)}) + async def read(self, path: Path) -> io.IOBase: + return await self._inner.read(path) + + @instrumented_op("write", data=_write_start_data) + async def write(self, path: Path, data: io.IOBase) -> None: + await self._inner.write(path, data) + + @instrumented_op( + "running", + finish_data=_running_finish_data, + ok=lambda _alive: True, + ) + async def running(self) -> bool: + return await self._inner.running() + + @instrumented_op( + "persist_workspace", + data=_persist_start_data, + finish_data=_persist_finish_data, + ) + async def persist_workspace(self) -> io.IOBase: + return await self._inner.persist_workspace() + + @instrumented_op( + "hydrate_workspace", + data=_hydrate_start_data, + ) + async def hydrate_workspace(self, data: io.IOBase) -> None: + await self._inner.hydrate_workspace(data) diff --git a/src/agents/sandbox/session/sandbox_session_state.py b/src/agents/sandbox/session/sandbox_session_state.py new file mode 100644 index 0000000000..97bd9929a6 --- /dev/null +++ b/src/agents/sandbox/session/sandbox_session_state.py @@ -0,0 +1,25 @@ +from __future__ import annotations + +import uuid + +from pydantic import BaseModel, ConfigDict, Field, field_serializer, field_validator + +from ..manifest import Manifest +from ..snapshot import SnapshotBase + + +class SandboxSessionState(BaseModel): + model_config = ConfigDict(arbitrary_types_allowed=True) + session_id: uuid.UUID = Field(default_factory=uuid.uuid4) + snapshot: SnapshotBase + manifest: Manifest + + @field_validator("snapshot", mode="before") + @classmethod + def _coerce_snapshot(cls, value: object) -> SnapshotBase: + return SnapshotBase.parse(value) + + @field_serializer("snapshot", when_used="json") + def _serialize_snapshot(self, snapshot: SnapshotBase) -> object: + # Ensure subclass fields (e.g. LocalSnapshot.base_path) are preserved in JSON. + return snapshot.model_dump(mode="json") diff --git a/src/agents/sandbox/session/sinks.py b/src/agents/sandbox/session/sinks.py new file mode 100644 index 0000000000..6401ea6d12 --- /dev/null +++ b/src/agents/sandbox/session/sinks.py @@ -0,0 +1,334 @@ +from __future__ import annotations + +import abc +import asyncio +import io +import logging +from pathlib import Path +from types import ModuleType +from typing import Callable, Literal, Protocol, runtime_checkable +from urllib.error import HTTPError, URLError +from urllib.request import Request, urlopen + +from ..entries import Dir +from .base_sandbox_session import BaseSandboxSession +from .events import EventPayloadPolicy, UCEvent +from .utils import event_to_json_line + +logger = logging.getLogger(__name__) + +DeliveryMode = Literal["sync", "async", "best_effort"] +OnErrorPolicy = Literal["raise", "log", "ignore"] + + +def _unwrap_session_wrapper(session: BaseSandboxSession) -> BaseSandboxSession: + """ + Defensive unwrapping: if a sink is accidentally bound to a SandboxSession wrapper, + unwrap to the underlying session to avoid recursive event loops. + """ + + # Avoid importing session.sandbox_session.SandboxSession here + # (would create a dependency cycle). + cls = type(session) + if not ( + cls.__name__ == "SandboxSession" + and cls.__module__ == "agents.sandbox.session.sandbox_session" + ): + return session + inner = getattr(session, "_inner", None) + return inner if isinstance(inner, BaseSandboxSession) else session + + +class EventSink(abc.ABC): + """Consumes UCEvent objects (e.g., callback, file outbox, proxy HTTP).""" + + name: str | None = None + mode: DeliveryMode + on_error: OnErrorPolicy + payload_policy: EventPayloadPolicy | None + + @abc.abstractmethod + async def handle(self, event: UCEvent) -> None: ... + + +@runtime_checkable +class SandboxSessionBoundSink(Protocol): + """Optional interface for sinks that need access to the underlying SandboxSession.""" + + def bind(self, session: BaseSandboxSession) -> None: ... + + +class CallbackSink(EventSink): + """Deliver events to a user-provided callable. + + Supports sync or async callables. + """ + + def __init__( + self, + callback: Callable[[UCEvent, BaseSandboxSession], object], + *, + mode: DeliveryMode = "sync", + on_error: OnErrorPolicy = "raise", + payload_policy: EventPayloadPolicy | None = None, + name: str | None = None, + ) -> None: + self._callback = callback + self.mode = mode + self.on_error = on_error + self.payload_policy = payload_policy + self._session: BaseSandboxSession | None = None + self.name = name + + def bind(self, session: BaseSandboxSession) -> None: + self._session = _unwrap_session_wrapper(session) + + async def handle(self, event: UCEvent) -> None: + if self._session is None: + raise RuntimeError( + "CallbackSink requires a bound session; use SandboxSession / " + "a sandbox client with instrumentation (or call bind(session))." + ) + out = self._callback(event, self._session) + if asyncio.iscoroutine(out): + await out + + +class JsonlOutboxSink(EventSink): + """Append events to a JSONL file on the host filesystem.""" + + def __init__( + self, + path: Path, + *, + mode: DeliveryMode = "best_effort", + on_error: OnErrorPolicy = "log", + payload_policy: EventPayloadPolicy | None = None, + ) -> None: + self.path = path + self.mode = mode + self.on_error = on_error + self.payload_policy = payload_policy + + async def handle(self, event: UCEvent) -> None: + line = event_to_json_line(event) + await asyncio.to_thread(self._append_line, line) + + def _append_line(self, line: str) -> None: + self.path.parent.mkdir(parents=True, exist_ok=True) + fcntl_mod: ModuleType | None + try: + import fcntl as fcntl_mod + except Exception: + # Not available on all platforms (e.g. Windows) + fcntl_mod = None + + with self.path.open("a", encoding="utf-8") as f: + if fcntl_mod is not None: + try: + fcntl_mod.flock(f.fileno(), fcntl_mod.LOCK_EX) + except Exception: + pass + f.write(line) + f.flush() + if fcntl_mod is not None: + try: + # Nice to have release here; the OS releases the lock + # automatically when the file is closed. + fcntl_mod.flock(f.fileno(), fcntl_mod.LOCK_UN) + except Exception: + pass + + +class WorkspaceJsonlSink(EventSink): + """ + Append events to a JSONL file inside the session workspace (under manifest.root). + + This sink still runs in the client process, but writes into the session via + `SandboxSession.write()`, so it works across sandboxes (Docker/Modal) + without requiring host-mounted volumes. + """ + + def __init__( + self, + *, + workspace_relpath: Path = Path("logs/events-{session_id}.jsonl"), + ephemeral: bool = False, + mode: DeliveryMode = "best_effort", + on_error: OnErrorPolicy = "log", + payload_policy: EventPayloadPolicy | None = None, + flush_every: int = 1, + ) -> None: + """ + Args: + workspace_relpath: Relative path under the session workspace root. + This also supports lightweight templating which is expanded on `bind()`: + - `"{session_id}"` (UUID string, e.g. "550e8400-e29b-41d4-a716-446655440000") + - `"{session_id_hex}"` (UUID hex, e.g. "550e8400e29b41d4a716446655440000") + + Example: + Path("logs/events-{session_id}.jsonl") + """ + self.workspace_relpath = workspace_relpath + self.ephemeral = ephemeral + self.mode = mode + self.on_error = on_error + self.payload_policy = payload_policy + self._session: BaseSandboxSession | None = None + self._resolved_workspace_relpath: Path | None = None + self._buf = bytearray() + self._seen = 0 + self._lock = asyncio.Lock() + self._flush_every = max(1, int(flush_every)) + + def _resolve_relpath(self) -> Path: + rel = self.workspace_relpath + if self._session is None: + return rel + template = str(rel) + try: + rendered = template.format( + session_id=self._session.state.session_id, + session_id_hex=self._session.state.session_id.hex, + ) + except Exception: + # If formatting fails for any reason, fall back to the literal path. + rendered = template + return Path(rendered) + + def bind(self, session: BaseSandboxSession) -> None: + self._session = _unwrap_session_wrapper(session) + self._resolved_workspace_relpath = self._resolve_relpath() + if self.ephemeral: + # Mark the parent dir as ephemeral so workspace persistence excludes the outbox. + relpath = self._resolved_workspace_relpath + parent = relpath.parent + key = str(parent) if str(parent) not in ("", ".") else str(relpath) + manifest = self._session.state.manifest + if key not in manifest.entries: + manifest.entries[key] = Dir( + ephemeral=True, + description="sandbox workspace events", + ) + + def _buffer_event(self, event: UCEvent) -> bool: + self._buf.extend(event_to_json_line(event).encode("utf-8")) + self._seen += 1 + + if self._seen % self._flush_every == 0: + return True + if event.op == "persist_workspace" and event.phase == "start": + return True + if event.op == "stop": + return True + if event.op == "shutdown" and event.phase == "start": + return True + if event.op == "shutdown" and event.phase == "finish": + return False + + return False + + async def _can_flush_to_workspace(self) -> bool: + if self._session is None: + return False + + # `SandboxSession.start()` emits the `start` event before the underlying sandbox + # is fully running, so writes may still fail during early startup or late teardown. + try: + return await self._session.running() + except Exception: + return False + + async def _flush_buffer(self) -> None: + if self._session is None: + return + + relpath = self._resolved_workspace_relpath or self.workspace_relpath + await self._session.write(relpath, io.BytesIO(bytes(self._buf))) + + async def handle(self, event: UCEvent) -> None: + # If unbound (e.g., Instrumentation.emit used without a SandboxSession wrapper), + # no-op. + if self._session is None: + return + + async with self._lock: + if not self._buffer_event(event): + return + + if not await self._can_flush_to_workspace(): + return + + await self._flush_buffer() + + +class HttpProxySink(EventSink): + """POST events as JSON to a proxy endpoint (local daemon or remote service).""" + + def __init__( + self, + endpoint: str, + *, + headers: dict[str, str] | None = None, + timeout_s: float = 5.0, + spool_path: Path | None = None, + mode: DeliveryMode = "best_effort", + on_error: OnErrorPolicy = "log", + payload_policy: EventPayloadPolicy | None = None, + ) -> None: + self.endpoint = endpoint + self.headers = headers or {} + self.timeout_s = timeout_s + self.spool_path = spool_path + self.mode = mode + self.on_error = on_error + self.payload_policy = payload_policy + + async def handle(self, event: UCEvent) -> None: + payload = event.model_dump_json().encode("utf-8") + spool_line = event_to_json_line(event) if self.spool_path is not None else None + await asyncio.to_thread(self._post, payload, spool_line) + + def _post(self, body: bytes, spool_line: str | None) -> None: + # TODO: thinking about using proxy instead of direct http call + req = Request( + self.endpoint, + data=body, + headers={"content-type": "application/json", **self.headers}, + method="POST", + ) + try: + with urlopen(req, timeout=self.timeout_s) as resp: + _ = resp.read(1) # ensure request completes + except (HTTPError, URLError) as e: + if spool_line is not None and self.spool_path is not None: + try: + self.spool_path.parent.mkdir(parents=True, exist_ok=True) + with self.spool_path.open("a", encoding="utf-8") as f: + f.write(spool_line) + f.flush() + except Exception: + pass + raise RuntimeError(f"http proxy sink POST failed: {e}") from e + + +class ChainedSink(EventSink): + """ + Groups multiple sinks that should run in order. + + Note: Instrumentation unwraps this group and applies per-op/per-sink payload policies to each + inner sink individually (so grouping does not disable per-sink policy behavior). + """ + + def __init__(self, *sinks: EventSink) -> None: + self.sinks = list(sinks) + # These are not used directly when Instrumentation unwraps the group, but keep the object + # conforming to EventSink. + self.mode = "sync" + self.on_error = "raise" + self.payload_policy = None + + async def handle(self, event: UCEvent) -> None: + # Fallback behavior if used directly (without Instrumentation unwrapping). + for sink in self.sinks: + await sink.handle(event) diff --git a/src/agents/sandbox/session/utils.py b/src/agents/sandbox/session/utils.py new file mode 100644 index 0000000000..c9f91e488d --- /dev/null +++ b/src/agents/sandbox/session/utils.py @@ -0,0 +1,37 @@ +from __future__ import annotations + +import io +import json +import uuid +from contextvars import ContextVar + +from .events import UCEvent + + +def _safe_decode(b: bytes, *, max_chars: int) -> str: + # Decode bytes as UTF-8 with replacement to keep event JSON valid. + # Truncation is on decoded string length, not raw bytes. + s = b.decode("utf-8", errors="replace") + if len(s) > max_chars: + return s[:max_chars] + "…" + return s + + +def _best_effort_stream_len(stream: io.IOBase) -> int | None: + # Avoid consuming the stream. This only works for seekable streams. + try: + pos = stream.tell() + stream.seek(0, io.SEEK_END) + end = stream.tell() + stream.seek(pos, io.SEEK_SET) + return int(end - pos) + except Exception: + return None + + +def event_to_json_line(event: UCEvent) -> str: + payload = event.model_dump(mode="json") + return json.dumps(payload, separators=(",", ":"), sort_keys=True) + "\n" + + +current_span_id: ContextVar[uuid.UUID | None] = ContextVar("uc_current_span_id", default=None) diff --git a/src/agents/sandbox/session/workspace_payloads.py b/src/agents/sandbox/session/workspace_payloads.py new file mode 100644 index 0000000000..c95dbf636c --- /dev/null +++ b/src/agents/sandbox/session/workspace_payloads.py @@ -0,0 +1,15 @@ +from __future__ import annotations + +import io +from pathlib import Path + +from ..errors import WorkspaceWriteTypeError + + +def coerce_write_payload(*, path: Path, data: io.IOBase) -> bytes: + payload = data.read() + if isinstance(payload, str): + payload = payload.encode("utf-8") + if not isinstance(payload, (bytes, bytearray)): + raise WorkspaceWriteTypeError(path=path, actual_type=type(payload).__name__) + return bytes(payload) diff --git a/src/agents/sandbox/snapshot.py b/src/agents/sandbox/snapshot.py new file mode 100644 index 0000000000..e7f89c30f6 --- /dev/null +++ b/src/agents/sandbox/snapshot.py @@ -0,0 +1,138 @@ +import abc +import io +import shutil +from pathlib import Path +from typing import Annotated, ClassVar, Literal + +from pydantic import BaseModel, Field, PrivateAttr + +from .errors import ( + SnapshotNotRestorableError, + SnapshotPersistError, + SnapshotRestoreError, +) + +SnapshotClass = type["SnapshotBase"] + + +class SnapshotBase(BaseModel, abc.ABC): + type: str + id: str + _subclass_registry: ClassVar[dict[str, SnapshotClass]] = {} + + @classmethod + def __pydantic_init_subclass__(cls, **kwargs: object) -> None: + super().__pydantic_init_subclass__(**kwargs) + + type_field = cls.model_fields.get("type") + type_default = type_field.default if type_field is not None else None + if not isinstance(type_default, str) or type_default == "": + raise TypeError(f"{cls.__name__} must define a non-empty string default for `type`") + + existing = SnapshotBase._subclass_registry.get(type_default) + if existing is not None and existing is not cls: + raise TypeError( + f"snapshot type `{type_default}` is already registered by {existing.__name__}" + ) + SnapshotBase._subclass_registry[type_default] = cls + + @classmethod + def parse(cls, payload: object) -> "SnapshotBase": + if isinstance(payload, SnapshotBase): + return payload + + if isinstance(payload, dict): + snapshot_type = payload.get("type") + if isinstance(snapshot_type, str): + snapshot_class = cls._snapshot_class_for_type(snapshot_type) + if snapshot_class is not None: + return snapshot_class.model_validate(payload) + + raise ValueError(f"unknown snapshot type `{snapshot_type}`") + + raise TypeError("snapshot payload must be a SnapshotBase or object payload") + + @classmethod + def _snapshot_class_for_type(cls, snapshot_type: str) -> SnapshotClass | None: + return SnapshotBase._subclass_registry.get(snapshot_type) + + @abc.abstractmethod + async def persist(self, data: io.IOBase) -> None: ... + + @abc.abstractmethod + async def restore(self) -> io.IOBase: ... + + @abc.abstractmethod + async def restorable(self) -> bool: ... + + +class LocalSnapshot(SnapshotBase): + type: Literal["local"] = "local" + + base_path: Path + _checksum: str | None = PrivateAttr(default=None) + + async def persist(self, data: io.IOBase) -> None: + path = self._path() + try: + path.parent.mkdir(parents=True, exist_ok=True) + with path.open("wb") as f: + shutil.copyfileobj(data, f) + except OSError as e: + raise SnapshotPersistError(snapshot_id=self.id, path=path, cause=e) from e + + async def restore(self) -> io.IOBase: + path = self._path() + try: + return path.open("rb") + except OSError as e: + raise SnapshotRestoreError(snapshot_id=self.id, path=path, cause=e) from e + + async def restorable(self) -> bool: + return self._path().exists() + + def _path(self) -> Path: + return Path(str(self.base_path / self.id) + ".tar") + + +class NoopSnapshot(SnapshotBase): + type: Literal["noop"] = "noop" + + async def persist(self, data: io.IOBase) -> None: + _ = data + return + + async def restore(self) -> io.IOBase: + raise SnapshotNotRestorableError(snapshot_id=self.id, path=Path("")) + + async def restorable(self) -> bool: + return False + + +class SnapshotSpec(BaseModel, abc.ABC): + type: str + + @abc.abstractmethod + def build(self, snapshot_id: str) -> SnapshotBase: ... + + +class LocalSnapshotSpec(SnapshotSpec): + type: Literal["local"] = "local" + base_path: Path + + def build(self, snapshot_id: str) -> SnapshotBase: + return LocalSnapshot(id=snapshot_id, base_path=self.base_path) + + +class NoopSnapshotSpec(SnapshotSpec): + type: Literal["noop"] = "noop" + + def build(self, snapshot_id: str) -> SnapshotBase: + return NoopSnapshot(id=snapshot_id) + + +SnapshotSpecUnion = Annotated[LocalSnapshotSpec | NoopSnapshotSpec, Field(discriminator="type")] + + +def resolve_snapshot(spec: SnapshotSpec | None, snapshot_id: str) -> SnapshotBase: + return (spec or NoopSnapshotSpec()).build(snapshot_id) diff --git a/src/agents/sandbox/snapshot_defaults.py b/src/agents/sandbox/snapshot_defaults.py new file mode 100644 index 0000000000..afe7b9c8a8 --- /dev/null +++ b/src/agents/sandbox/snapshot_defaults.py @@ -0,0 +1,89 @@ +from __future__ import annotations + +import os +import sys +import time +from collections.abc import Mapping +from pathlib import Path + +from .snapshot import LocalSnapshotSpec + +_DEFAULT_LOCAL_SNAPSHOT_TTL_SECONDS = 60 * 60 * 24 * 30 +_DEFAULT_LOCAL_SNAPSHOT_SUBDIR = Path("openai-agents-python") / "sandbox" / "snapshots" + + +def default_local_snapshot_base_dir( + *, + home: Path | None = None, + env: Mapping[str, str] | None = None, + platform: str | None = None, + os_name: str | None = None, +) -> Path: + resolved_home = home or Path.home() + resolved_env = env or os.environ + resolved_platform = platform or sys.platform + resolved_os_name = os_name or os.name + + if resolved_platform == "darwin": + base = resolved_home / "Library" / "Application Support" + elif resolved_os_name == "nt": + local_app_data = resolved_env.get("LOCALAPPDATA") or resolved_env.get("APPDATA") + base = Path(local_app_data) if local_app_data else resolved_home / "AppData" / "Local" + else: + xdg_state_home = resolved_env.get("XDG_STATE_HOME") + base = Path(xdg_state_home) if xdg_state_home else resolved_home / ".local" / "state" + + return base / _DEFAULT_LOCAL_SNAPSHOT_SUBDIR + + +def cleanup_stale_default_local_snapshots( + base_path: Path, + *, + now: float | None = None, + max_age_seconds: int = _DEFAULT_LOCAL_SNAPSHOT_TTL_SECONDS, +) -> None: + # This is intentionally limited to stale files in the SDK-managed default directory. + # We do not delete snapshots during normal session teardown because pause/resume may still + # need them. If we add explicit artifact cleanup later, it should be a separate opt-in path + # that can also account for backend-specific remote artifacts. + if max_age_seconds < 0 or not base_path.exists(): + return + + cutoff = (time.time() if now is None else now) - max_age_seconds + try: + candidates = list(base_path.glob("*.tar")) + except OSError: + return + + for candidate in candidates: + try: + if not candidate.is_file(): + continue + if candidate.stat().st_mtime >= cutoff: + continue + candidate.unlink(missing_ok=True) + except OSError: + continue + + +def resolve_default_local_snapshot_spec( + *, + home: Path | None = None, + env: Mapping[str, str] | None = None, + platform: str | None = None, + os_name: str | None = None, + now: float | None = None, +) -> LocalSnapshotSpec: + base_path = default_local_snapshot_base_dir( + home=home, + env=env, + platform=platform, + os_name=os_name, + ) + base_path.mkdir(parents=True, exist_ok=True, mode=0o700) + if (os_name or os.name) != "nt": + try: + base_path.chmod(0o700) + except OSError: + pass + return LocalSnapshotSpec(base_path=base_path) diff --git a/src/agents/sandbox/types.py b/src/agents/sandbox/types.py new file mode 100644 index 0000000000..2c3ad15d04 --- /dev/null +++ b/src/agents/sandbox/types.py @@ -0,0 +1,147 @@ +import stat +from enum import IntEnum + +from pydantic import BaseModel, Field +from typing_extensions import Self + + +class User(BaseModel): + name: str + + def __hash__(self) -> int: + return hash(self.name) + + def __eq__(self, other: object) -> bool: + if not isinstance(other, User): + return NotImplemented + return self.name == other.name + + +class Group(BaseModel): + name: str + users: list[User] + + def __hash__(self) -> int: + return hash(self.name) + + def __eq__(self, other: object) -> bool: + if not isinstance(other, Group): + return NotImplemented + return self.name == other.name + + +class Permissions(BaseModel): + owner: int = Field(default=0o7) + group: int = Field(default=0) + other: int = Field(default=0) + directory: bool = Field(default=False) + + def to_mode(self) -> int: + mode = 0 + for perms, shift in [(self.owner, 6), (self.group, 3), (self.other, 0)]: + mode |= int(perms) << shift + if self.directory: + mode |= stat.S_IFDIR + return mode + + @classmethod + def from_mode(cls, mode: int) -> "Permissions": + return cls( + owner=(mode >> 6) & 0b111, + group=(mode >> 3) & 0b111, + other=(mode >> 0) & 0b111, + directory=bool(mode & stat.S_IFDIR), + ) + + @classmethod + def from_str(cls, perms: str) -> "Permissions": + if len(perms) == 11 and perms[-1] in {"@", "+"}: + perms = perms[:-1] + if len(perms) != 10: + raise ValueError(f"invalid permissions string length: {perms!r}") + + directory = perms[0] == "d" + if perms[0] not in {"d", "-"}: + raise ValueError(f"invalid permissions type: {perms!r}") + + def parse_triplet(triplet: str) -> int: + if len(triplet) != 3: + raise ValueError(f"invalid permissions triplet: {triplet!r}") + mask = 0 + if triplet[0] == "r": + mask |= FileMode.READ + elif triplet[0] != "-": + raise ValueError(f"invalid read flag: {triplet!r}") + if triplet[1] == "w": + mask |= FileMode.WRITE + elif triplet[1] != "-": + raise ValueError(f"invalid write flag: {triplet!r}") + if triplet[2] == "x": + mask |= FileMode.EXEC + elif triplet[2] != "-": + raise ValueError(f"invalid exec flag: {triplet!r}") + return int(mask) + + owner = parse_triplet(perms[1:4]) + group = parse_triplet(perms[4:7]) + other = parse_triplet(perms[7:10]) + return cls( + owner=owner, + group=group, + other=other, + directory=directory, + ) + + def owner_can(self, mode: int) -> Self: + self.owner = mode + return self + + def group_can(self, mode: int) -> Self: + self.group = mode + return self + + def others_can(self, mode: int) -> Self: + self.other = mode + return self + + def __repr__(self) -> str: + def fmt(perms: int) -> str: + return "".join( + c if perms & p else "-" + for p, c in [(FileMode.READ, "r"), (FileMode.WRITE, "w"), (FileMode.EXEC, "x")] + ) + + return ("d" if self.directory else "-") + "".join( + fmt(perms) for perms in (self.owner, self.group, self.other) + ) + + def __str__(self) -> str: + return repr(self) + + def __eq__(self, other: object) -> bool: + if not isinstance(other, Permissions): + return NotImplemented + return self.to_mode() == other.to_mode() + + +class FileMode(IntEnum): + ALL = 0o7 + NONE = 0 + + READ = 1 << 2 + WRITE = 1 << 1 + EXEC = 1 + + +class ExecResult: + stdout: bytes + stderr: bytes + exit_code: int + + def __init__(self, *, stdout: bytes, stderr: bytes, exit_code: int) -> None: + self.stdout = stdout + self.stderr = stderr + self.exit_code = exit_code + + def ok(self) -> bool: + return self.exit_code == 0 diff --git a/src/agents/sandbox/util/__init__.py b/src/agents/sandbox/util/__init__.py new file mode 100644 index 0000000000..13c9850a70 --- /dev/null +++ b/src/agents/sandbox/util/__init__.py @@ -0,0 +1,42 @@ +from .deep_merge import deep_merge +from .github import clone_repo, ensure_git_available +from .parse_utils import parse_ls_la +from .retry import ( + DEFAULT_TRANSIENT_RETRY_BACKOFF, + DEFAULT_TRANSIENT_RETRY_INTERVAL_S, + DEFAULT_TRANSIENT_RETRY_MAX_ATTEMPT, + TRANSIENT_HTTP_STATUS_CODES, + BackoffStrategy, + exception_chain_contains_type, + exception_chain_has_status_code, + iter_exception_chain, + retry_async, +) +from .tar_utils import ( + UnsafeTarMemberError, + safe_extract_tarfile, + safe_tar_member_rel_path, + should_skip_tar_member, + validate_tarfile, +) + +__all__ = [ + "DEFAULT_TRANSIENT_RETRY_BACKOFF", + "DEFAULT_TRANSIENT_RETRY_INTERVAL_S", + "DEFAULT_TRANSIENT_RETRY_MAX_ATTEMPT", + "BackoffStrategy", + "TRANSIENT_HTTP_STATUS_CODES", + "exception_chain_contains_type", + "exception_chain_has_status_code", + "iter_exception_chain", + "retry_async", + "deep_merge", + "clone_repo", + "ensure_git_available", + "parse_ls_la", + "UnsafeTarMemberError", + "safe_extract_tarfile", + "safe_tar_member_rel_path", + "should_skip_tar_member", + "validate_tarfile", +] diff --git a/src/agents/sandbox/util/checksums.py b/src/agents/sandbox/util/checksums.py new file mode 100644 index 0000000000..0b47f3d173 --- /dev/null +++ b/src/agents/sandbox/util/checksums.py @@ -0,0 +1,15 @@ +from __future__ import annotations + +import hashlib +from pathlib import Path + + +def sha256_file(path: Path) -> str: + digest = hashlib.sha256() + with path.open("rb") as handle: + while True: + chunk = handle.read(1024 * 1024) + if not chunk: + break + digest.update(chunk) + return digest.hexdigest() diff --git a/src/agents/sandbox/util/deep_merge.py b/src/agents/sandbox/util/deep_merge.py new file mode 100644 index 0000000000..d8aa96b160 --- /dev/null +++ b/src/agents/sandbox/util/deep_merge.py @@ -0,0 +1,21 @@ +from typing import TypeGuard + + +def _is_string_object_dict(value: object) -> TypeGuard[dict[str, object]]: + return isinstance(value, dict) and all(isinstance(key, str) for key in value) + + +def deep_merge(dict1: dict[str, object], dict2: dict[str, object]) -> dict[str, object]: + """ + Recursively merge dict2 into dict1 and return a new dict. + If both values for a key are dicts, merge them. + Otherwise, dict2's value overwrites dict1's. + """ + result = dict1.copy() + for key, value in dict2.items(): + existing = result.get(key) + if _is_string_object_dict(existing) and _is_string_object_dict(value): + result[key] = deep_merge(existing, value) + else: + result[key] = value + return result diff --git a/src/agents/sandbox/util/github.py b/src/agents/sandbox/util/github.py new file mode 100644 index 0000000000..4a35462158 --- /dev/null +++ b/src/agents/sandbox/util/github.py @@ -0,0 +1,53 @@ +from __future__ import annotations + +import shutil +import subprocess +from pathlib import Path + + +def ensure_git_available() -> None: + if shutil.which("git") is None: + raise RuntimeError("git is required to use github_repo artifacts") + + +def clone_repo(*, repo: str, ref: str, dest: Path) -> None: + """Shallow clone a GitHub repo at a ref (tag/branch/sha).""" + + ensure_git_available() + url = f"https://github.com/{repo}.git" + dest.parent.mkdir(parents=True, exist_ok=True) + + # Use a shallow clone for tags/branches; fall back to a pinned checkout for SHAs. + try: + subprocess.run( + [ + "git", + "clone", + "--depth", + "1", + "--no-tags", + "--branch", + ref, + url, + str(dest), + ], + check=True, + stdout=subprocess.DEVNULL, + stderr=subprocess.DEVNULL, + ) + return + except subprocess.CalledProcessError: + pass + + subprocess.run( + ["git", "clone", "--no-checkout", url, str(dest)], + check=True, + stdout=subprocess.DEVNULL, + stderr=subprocess.DEVNULL, + ) + subprocess.run( + ["git", "-C", str(dest), "checkout", ref], + check=True, + stdout=subprocess.DEVNULL, + stderr=subprocess.DEVNULL, + ) diff --git a/src/agents/sandbox/util/iterator_io.py b/src/agents/sandbox/util/iterator_io.py new file mode 100644 index 0000000000..cf4119543e --- /dev/null +++ b/src/agents/sandbox/util/iterator_io.py @@ -0,0 +1,69 @@ +import io +from collections.abc import Iterator + + +class IteratorIO(io.IOBase): + def __init__(self, it: Iterator[bytes]): + self._it = it + self._buffer = bytearray() + self._closed = False + + def readable(self) -> bool: + return True + + def read(self, size: int = -1) -> bytes: + if self._closed: + return b"" + + if size < 0: + # Read all remaining data. + chunks: list[bytes] = [] + if self._buffer: + chunks.append(bytes(self._buffer)) + self._buffer.clear() + for chunk in self._it: + if chunk: + chunks.append(chunk) + self._closed = True + return b"".join(chunks) + + if size == 0: + return b"" + + # Fill buffer until we can satisfy the request or iterator is exhausted. + while len(self._buffer) < size and not self._closed: + try: + chunk = next(self._it) + if not chunk: + continue + self._buffer.extend(chunk) + except StopIteration: + self._closed = True + + out = bytes(self._buffer[:size]) + del self._buffer[:size] + return out + + def readinto(self, b: bytearray) -> int: + if self._closed: + return 0 + + # Fill buffer until we have something or iterator is exhausted + while not self._buffer: + try: + chunk = next(self._it) + if not chunk: + continue + self._buffer.extend(chunk) + except StopIteration: + self._closed = True + return 0 + + n = min(len(b), len(self._buffer)) + b[:n] = self._buffer[:n] + del self._buffer[:n] + return n + + def close(self) -> None: + self._closed = True + super().close() diff --git a/src/agents/sandbox/util/parse_utils.py b/src/agents/sandbox/util/parse_utils.py new file mode 100644 index 0000000000..6c27705793 --- /dev/null +++ b/src/agents/sandbox/util/parse_utils.py @@ -0,0 +1,59 @@ +from ..files import EntryKind, FileEntry +from ..types import Permissions + + +def parse_ls_la(output: str, *, base: str) -> list[FileEntry]: + entries: list[FileEntry] = [] + for raw_line in output.splitlines(): + line = raw_line.strip("\n") + if not line or line.startswith("total"): + continue + + # Typical coreutils format: + # drwxr-xr-x 2 root root 4096 Jan 1 00:00 dirname + # -rw-r--r-- 1 root root 123 Jan 1 00:00 file.txt + # lrwxrwxrwx 1 root root 12 Jan 1 00:00 link -> target + parts = line.split(maxsplit=8) + if len(parts) < 9: + continue + + permissions_str = parts[0] + owner = parts[2] + group = parts[3] + try: + size = int(parts[4]) + except ValueError: + continue + + kind_map: dict[str, EntryKind] = { + "d": EntryKind.DIRECTORY, + "-": EntryKind.FILE, + "l": EntryKind.SYMLINK, + } + kind: EntryKind = kind_map.get(permissions_str[:1], EntryKind.OTHER) + + # Permissions only track rwx bits and directory-ness; for symlink/other entries we + # preserve rwx bits by normalizing the leading type marker to "-". + if permissions_str[:1] not in {"d", "-"} and len(permissions_str) >= 2: + permissions_str = "-" + permissions_str[1:] + + name = parts[8] + if " -> " in name: + name = name.split(" -> ", 1)[0] + + if name in {".", ".."}: + continue + + permissions = Permissions.from_str(permissions_str) + entries.append( + FileEntry( + path=f"{base.rstrip('/')}/{name}" if base != "/" else f"/{name}", + permissions=permissions, + owner=owner, + group=group, + size=size, + kind=kind, + ) + ) + + return entries diff --git a/src/agents/sandbox/util/retry.py b/src/agents/sandbox/util/retry.py new file mode 100644 index 0000000000..889058bd6d --- /dev/null +++ b/src/agents/sandbox/util/retry.py @@ -0,0 +1,127 @@ +from __future__ import annotations + +import asyncio +import functools +import inspect +from collections.abc import Callable, Coroutine, Iterable +from enum import Enum +from typing import ParamSpec, TypeVar, cast + +P = ParamSpec("P") +T = TypeVar("T") + + +class BackoffStrategy(str, Enum): + def __str__(self) -> str: + return str(self.value) + + FIXED = "fixed" + LINEAR = "linear" + EXPONENTIAL = "exponential" + + +DEFAULT_TRANSIENT_RETRY_INTERVAL_S = 0.25 +DEFAULT_TRANSIENT_RETRY_MAX_ATTEMPT = 3 +DEFAULT_TRANSIENT_RETRY_BACKOFF = BackoffStrategy.EXPONENTIAL +TRANSIENT_HTTP_STATUS_CODES: frozenset[int] = frozenset({500, 502, 503, 504}) + + +def iter_exception_chain(exc: BaseException) -> Iterable[BaseException]: + seen: set[int] = set() + current: BaseException | None = exc + while current is not None and id(current) not in seen: + yield current + seen.add(id(current)) + current = cast( + BaseException | None, + getattr(current, "__cause__", None) or getattr(current, "__context__", None), + ) + + +def exception_chain_contains_type( + exc: BaseException, + error_types: tuple[type[BaseException], ...], +) -> bool: + if not error_types: + return False + return any(isinstance(candidate, error_types) for candidate in iter_exception_chain(exc)) + + +def exception_chain_has_status_code( + exc: BaseException, + status_codes: set[int] | frozenset[int], +) -> bool: + for candidate in iter_exception_chain(exc): + for value in ( + getattr(candidate, "status_code", None), + getattr(candidate, "http_code", None), + getattr(getattr(candidate, "response", None), "status_code", None), + ): + if isinstance(value, int) and value in status_codes: + return True + return False + + +def retry_async( + *, + interval: float = DEFAULT_TRANSIENT_RETRY_INTERVAL_S, + max_attempt: int = DEFAULT_TRANSIENT_RETRY_MAX_ATTEMPT, + backoff: BackoffStrategy = DEFAULT_TRANSIENT_RETRY_BACKOFF, + retry_if: Callable[..., bool], + on_retry: Callable[..., object] | None = None, +) -> Callable[ + [Callable[P, Coroutine[object, object, T]]], + Callable[P, Coroutine[object, object, T]], +]: + """Retry an async function when `retry_if` marks the exception as transient. + + `backoff=BackoffStrategy.FIXED` keeps a constant delay equal to `interval`. + `backoff=BackoffStrategy.LINEAR` scales delay as `interval * attempt`. + `backoff=BackoffStrategy.EXPONENTIAL` doubles the delay on each retry attempt. + """ + + if max_attempt < 1: + raise ValueError("max_attempt must be >= 1") + if interval < 0: + raise ValueError("interval must be >= 0") + if backoff not in { + BackoffStrategy.FIXED, + BackoffStrategy.LINEAR, + BackoffStrategy.EXPONENTIAL, + }: + raise ValueError( + "backoff must be BackoffStrategy.FIXED, " + "BackoffStrategy.LINEAR, or BackoffStrategy.EXPONENTIAL" + ) + + def decorator( + fn: Callable[P, Coroutine[object, object, T]], + ) -> Callable[P, Coroutine[object, object, T]]: + @functools.wraps(fn) + async def wrapped(*args: P.args, **kwargs: P.kwargs) -> T: + for attempt in range(1, max_attempt + 1): + try: + return await fn(*args, **kwargs) + except Exception as exc: + if attempt >= max_attempt or not retry_if(exc, *args, **kwargs): + raise + + if backoff is BackoffStrategy.EXPONENTIAL: + delay_s = interval * (2 ** (attempt - 1)) + elif backoff is BackoffStrategy.LINEAR: + delay_s = interval * attempt + else: + delay_s = interval + + if on_retry is not None: + hook_result = on_retry(exc, attempt, max_attempt, delay_s, *args, **kwargs) + if inspect.isawaitable(hook_result): + await hook_result + + await asyncio.sleep(delay_s) + + raise AssertionError("unreachable") + + return cast(Callable[P, Coroutine[object, object, T]], wrapped) + + return decorator diff --git a/src/agents/sandbox/util/tar_utils.py b/src/agents/sandbox/util/tar_utils.py new file mode 100644 index 0000000000..6240ad9186 --- /dev/null +++ b/src/agents/sandbox/util/tar_utils.py @@ -0,0 +1,150 @@ +from __future__ import annotations + +import os +import shutil +import tarfile +from collections.abc import Iterable +from pathlib import Path, PurePosixPath + + +class UnsafeTarMemberError(ValueError): + def __init__(self, *, member: str, reason: str) -> None: + super().__init__(f"unsafe tar member {member!r}: {reason}") + self.member = member + self.reason = reason + + +def safe_tar_member_rel_path(member: tarfile.TarInfo) -> Path | None: + if member.name in ("", ".", "./"): + return None + rel = PurePosixPath(member.name) + if rel.is_absolute(): + raise UnsafeTarMemberError(member=member.name, reason="absolute path") + if ".." in rel.parts: + raise UnsafeTarMemberError(member=member.name, reason="parent traversal") + if member.issym() or member.islnk(): + raise UnsafeTarMemberError(member=member.name, reason="link member not allowed") + if not (member.isdir() or member.isreg()): + raise UnsafeTarMemberError(member=member.name, reason="unsupported member type") + return Path(*rel.parts) + + +def _normalize_rel(prefix: str | Path) -> Path: + rel = prefix if isinstance(prefix, Path) else Path(prefix) + posix = rel.as_posix() + parts = [p for p in Path(posix).parts if p not in ("", ".")] + if parts[:1] == ["/"]: + parts = parts[1:] + return Path(*parts) + + +def _is_within(path: Path, prefix: Path) -> bool: + if prefix == Path(): + return True + if path == prefix: + return True + return path.parts[: len(prefix.parts)] == prefix.parts + + +def should_skip_tar_member( + member_name: str, + *, + skip_rel_paths: Iterable[str | Path], + root_name: str | None, +) -> bool: + """ + Decide whether a tar member should be excluded based on workspace-relative prefixes. + + `member_name` is the raw name from the tar, which may include `.` or the workspace root + directory name depending on how the tar was produced. + """ + + raw_parts = [p for p in Path(member_name).parts if p not in ("", ".")] + if raw_parts[:1] == ["/"]: + raw_parts = raw_parts[1:] + if not raw_parts: + rel_variants = [Path()] + else: + rel_variants = [Path(*raw_parts)] + if root_name and raw_parts and raw_parts[0] == root_name: + rel_variants.append(Path(*raw_parts[1:])) + + prefixes = [_normalize_rel(p) for p in skip_rel_paths] + return any(_is_within(rel, prefix) for rel in rel_variants for prefix in prefixes) + + +def _ensure_no_symlink_parents(*, root: Path, dest: Path) -> None: + """ + Ensure that no existing parent directory in `dest` is a symlink. + + This helps prevent writing outside `root` via pre-existing symlink components. + """ + + root_resolved = root.resolve() + dest_resolved = dest.resolve() + if not (dest_resolved == root_resolved or dest_resolved.is_relative_to(root_resolved)): + raise UnsafeTarMemberError(member=str(dest), reason="path escapes root after resolution") + + rel = dest.relative_to(root) + cur = root + for part in rel.parts[:-1]: + cur = cur / part + if cur.exists() and cur.is_symlink(): + raise UnsafeTarMemberError(member=str(rel.as_posix()), reason="symlink in parent path") + + +def validate_tarfile(tar: tarfile.TarFile) -> None: + for member in tar.getmembers(): + safe_tar_member_rel_path(member) + + +def safe_extract_tarfile(tar: tarfile.TarFile, *, root: Path) -> None: + """ + Safely extract a tar archive into `root`. + + This rejects: + - absolute member paths + - paths containing `..` + - symlinks / hardlinks + - non-regular-file and non-directory members (devices, fifos, etc.) + + It also ensures extraction doesn't traverse through existing symlink parents. + """ + + root.mkdir(parents=True, exist_ok=True) + root_resolved = root.resolve() + + validate_tarfile(tar) + for member in tar.getmembers(): + name = member.name + rel_path = safe_tar_member_rel_path(member) + if rel_path is None: + continue + + dest = root_resolved / rel_path + _ensure_no_symlink_parents(root=root_resolved, dest=dest) + + if member.isdir(): + dest.mkdir(parents=True, exist_ok=True) + continue + + # Regular file + fileobj = tar.extractfile(member) + if fileobj is None: + raise UnsafeTarMemberError(member=name, reason="missing file payload") + + dest.parent.mkdir(parents=True, exist_ok=True) + _ensure_no_symlink_parents(root=root_resolved, dest=dest) + + flags = os.O_WRONLY | os.O_CREAT | os.O_TRUNC + if hasattr(os, "O_NOFOLLOW"): + flags |= os.O_NOFOLLOW + fd = os.open(dest, flags, 0o600) + try: + with os.fdopen(fd, "wb") as out: + shutil.copyfileobj(fileobj, out) + finally: + try: + fileobj.close() + except Exception: + pass diff --git a/tests/extensions/experiemental/codex/test_codex_tool.py b/tests/extensions/experiemental/codex/test_codex_tool.py index b9a78c7d0f..a133c3c775 100644 --- a/tests/extensions/experiemental/codex/test_codex_tool.py +++ b/tests/extensions/experiemental/codex/test_codex_tool.py @@ -27,6 +27,7 @@ from agents.lifecycle import RunHooks from agents.run_config import RunConfig from agents.run_context import RunContextWrapper +from agents.run_internal.agent_bindings import bind_public_agent from agents.run_internal.run_steps import ToolRunFunction from agents.run_internal.tool_execution import execute_function_tool_calls from agents.tool_context import ToolContext @@ -920,7 +921,7 @@ async def _error_tool() -> str: with pytest.raises(UserError, match="Error running tool error_tool: boom"): await execute_function_tool_calls( - agent=agent, + bindings=bind_public_agent(agent), tool_runs=tool_runs, hooks=RunHooks(), context_wrapper=context_wrapper, diff --git a/tests/extensions/test_sandbox_e2b.py b/tests/extensions/test_sandbox_e2b.py new file mode 100644 index 0000000000..ca6dad1858 --- /dev/null +++ b/tests/extensions/test_sandbox_e2b.py @@ -0,0 +1,309 @@ +from __future__ import annotations + +import base64 +import io +import tarfile +import uuid +from pathlib import Path +from typing import Literal + +import pytest +from pydantic import PrivateAttr + +from agents.extensions.sandbox.sandboxes.e2b import E2BSandboxSession, E2BSandboxSessionState +from agents.sandbox import Manifest +from agents.sandbox.entries import Mount +from agents.sandbox.errors import ( + WorkspaceArchiveReadError, + WorkspaceArchiveWriteError, + WorkspaceStartError, +) +from agents.sandbox.snapshot import NoopSnapshot + + +class _FakeE2BResult: + def __init__(self, *, stdout: str = "", stderr: str = "", exit_code: int = 0) -> None: + self.stdout = stdout + self.stderr = stderr + self.exit_code = exit_code + + +class _FakeE2BFiles: + def write( + self, + path: str, + data: bytes, + request_timeout: float | None = None, + ) -> None: + _ = (path, data, request_timeout) + + def remove(self, path: str, request_timeout: float | None = None) -> None: + _ = (path, request_timeout) + + def read(self, path: str, format: str = "bytes") -> bytes: + _ = (path, format) + return b"" + + +class _FakeE2BCommands: + def __init__(self) -> None: + self.exec_root_ready = False + self.calls: list[dict[str, object]] = [] + self.mkdir_result: _FakeE2BResult | None = None + self.next_result = _FakeE2BResult() + + def run( + self, + command: str, + timeout: float | None = None, + cwd: str | None = None, + envs: dict[str, str] | None = None, + user: str | None = None, + ) -> _FakeE2BResult: + self.calls.append( + { + "command": command, + "timeout": timeout, + "cwd": cwd, + "envs": envs, + "user": user, + } + ) + if command == "mkdir -p -- /workspace" and cwd == "/": + result = self.mkdir_result or _FakeE2BResult() + if result.exit_code == 0: + self.exec_root_ready = True + self.mkdir_result = None + return result + if cwd == "/workspace" and not self.exec_root_ready: + raise ValueError("cwd '/workspace' does not exist") + result = self.next_result + self.next_result = _FakeE2BResult() + return result + + +class _FakeE2BSandbox: + def __init__(self) -> None: + self.sandbox_id = "sb-123" + self.files = _FakeE2BFiles() + self.commands = _FakeE2BCommands() + + def beta_pause(self) -> None: + return + + def kill(self) -> None: + return + + def is_running(self, request_timeout: float | None = None) -> bool: + _ = request_timeout + return True + + +class _RecordingMount(Mount): + type: Literal["recording_mount"] = "recording_mount" + _mounted_paths: list[Path] = PrivateAttr(default_factory=list) + _unmounted_paths: list[Path] = PrivateAttr(default_factory=list) + + async def _mount(self, session: object, path: Path) -> None: + _ = session + self._mounted_paths.append(path) + + async def _unmount(self, session: object, path: Path) -> None: + _ = session + self._unmounted_paths.append(path) + + +def _session(*, workspace_root_ready: bool = False) -> tuple[E2BSandboxSession, _FakeE2BSandbox]: + sandbox = _FakeE2BSandbox() + state = E2BSandboxSessionState( + session_id=uuid.uuid4(), + manifest=Manifest(root="/workspace"), + snapshot=NoopSnapshot(id="snapshot"), + sandbox_id=sandbox.sandbox_id, + workspace_root_ready=workspace_root_ready, + ) + return E2BSandboxSession.from_state(state, sandbox=sandbox), sandbox + + +def _tar_bytes() -> bytes: + buf = io.BytesIO() + with tarfile.open(fileobj=buf, mode="w") as tar: + info = tarfile.TarInfo("note.txt") + payload = b"hello" + info.size = len(payload) + tar.addfile(info, io.BytesIO(payload)) + return buf.getvalue() + + +@pytest.mark.asyncio +async def test_e2b_exec_omits_cwd_until_workspace_ready() -> None: + session, sandbox = _session(workspace_root_ready=False) + + result = await session._exec_internal("find", ".", timeout=0.01) # noqa: SLF001 + + assert result.ok() + assert sandbox.commands.calls == [ + { + "command": "find .", + "timeout": 0.01, + "cwd": None, + "envs": {}, + "user": None, + } + ] + + +@pytest.mark.asyncio +async def test_e2b_exec_uses_manifest_root_after_workspace_ready() -> None: + session, sandbox = _session(workspace_root_ready=True) + sandbox.commands.exec_root_ready = True + + result = await session._exec_internal("find", ".", timeout=0.01) # noqa: SLF001 + + assert result.ok() + assert sandbox.commands.calls == [ + { + "command": "find .", + "timeout": 0.01, + "cwd": "/workspace", + "envs": {}, + "user": None, + } + ] + + +@pytest.mark.asyncio +async def test_e2b_start_prepares_workspace_root_for_command_cwd() -> None: + session, sandbox = _session(workspace_root_ready=False) + + await session.start() + result = await session._exec_internal("pwd", timeout=0.01) # noqa: SLF001 + + assert result.ok() + assert session.state.workspace_root_ready is True + assert session._workspace_root_ready is True # noqa: SLF001 + assert sandbox.commands.calls == [ + { + "command": "mkdir -p -- /workspace", + "timeout": 10, + "cwd": "/", + "envs": {}, + "user": None, + }, + { + "command": "pwd", + "timeout": 0.01, + "cwd": "/workspace", + "envs": {}, + "user": None, + }, + ] + + +@pytest.mark.asyncio +async def test_e2b_start_raises_on_nonzero_workspace_root_setup_exit() -> None: + session, sandbox = _session(workspace_root_ready=False) + sandbox.commands.mkdir_result = _FakeE2BResult(stderr="mkdir failed", exit_code=2) + + with pytest.raises(WorkspaceStartError) as exc_info: + await session.start() + + assert exc_info.value.context["reason"] == "workspace_root_nonzero_exit" + assert exc_info.value.context["exit_code"] == 2 + assert session.state.workspace_root_ready is False + assert session._workspace_root_ready is False # noqa: SLF001 + + +@pytest.mark.asyncio +async def test_e2b_skip_start_still_prepares_workspace_root_for_resumed_exec_cwd() -> None: + session, sandbox = _session(workspace_root_ready=False) + session._skip_start = True # noqa: SLF001 + + await session.start() + result = await session._exec_internal("pwd", timeout=0.01) # noqa: SLF001 + + assert result.ok() + assert session.state.workspace_root_ready is True + assert session._workspace_root_ready is True # noqa: SLF001 + assert sandbox.commands.calls == [ + { + "command": "mkdir -p -- /workspace", + "timeout": 10, + "cwd": "/", + "envs": {}, + "user": None, + }, + { + "command": "pwd", + "timeout": 0.01, + "cwd": "/workspace", + "envs": {}, + "user": None, + }, + ] + + +@pytest.mark.asyncio +async def test_e2b_running_requires_workspace_root_ready() -> None: + session, _sandbox = _session(workspace_root_ready=False) + + assert await session.running() is False + + +@pytest.mark.asyncio +async def test_e2b_running_checks_remote_after_workspace_ready() -> None: + session, sandbox = _session(workspace_root_ready=True) + sandbox.commands.exec_root_ready = True + + assert await session.running() is True + + +@pytest.mark.asyncio +async def test_e2b_persist_workspace_raises_on_nonzero_snapshot_exit() -> None: + session, sandbox = _session(workspace_root_ready=True) + sandbox.commands.exec_root_ready = True + sandbox.commands.next_result = _FakeE2BResult(stderr="tar failed", exit_code=2) + + with pytest.raises(WorkspaceArchiveReadError) as exc_info: + await session.persist_workspace() + + assert exc_info.value.context["reason"] == "snapshot_nonzero_exit" + assert exc_info.value.context["exit_code"] == 2 + + +@pytest.mark.asyncio +async def test_e2b_hydrate_workspace_raises_on_nonzero_extract_exit() -> None: + session, sandbox = _session(workspace_root_ready=False) + sandbox.commands.next_result = _FakeE2BResult(stderr="tar failed", exit_code=2) + + with pytest.raises(WorkspaceArchiveWriteError) as exc_info: + await session.hydrate_workspace(io.BytesIO(_tar_bytes())) + + assert exc_info.value.context["reason"] == "hydrate_nonzero_exit" + assert exc_info.value.context["exit_code"] == 2 + assert session.state.workspace_root_ready is False + assert session._workspace_root_ready is False # noqa: SLF001 + + +@pytest.mark.asyncio +async def test_e2b_persist_workspace_remounts_mounts_after_snapshot() -> None: + mount = _RecordingMount() + sandbox = _FakeE2BSandbox() + sandbox.commands.exec_root_ready = True + sandbox.commands.next_result = _FakeE2BResult( + stdout=base64.b64encode(b"fake-tar-bytes").decode("ascii") + ) + state = E2BSandboxSessionState( + session_id=uuid.uuid4(), + manifest=Manifest(root="/workspace", entries={"mount": mount}), + snapshot=NoopSnapshot(id="snapshot"), + sandbox_id=sandbox.sandbox_id, + workspace_root_ready=True, + ) + session = E2BSandboxSession.from_state(state, sandbox=sandbox) + + archive = await session.persist_workspace() + + assert archive.read() == b"fake-tar-bytes" + assert mount._unmounted_paths == [Path("/workspace/mount")] + assert mount._mounted_paths == [Path("/workspace/mount")] diff --git a/tests/extensions/test_sandbox_modal.py b/tests/extensions/test_sandbox_modal.py new file mode 100644 index 0000000000..d68edf64c8 --- /dev/null +++ b/tests/extensions/test_sandbox_modal.py @@ -0,0 +1,215 @@ +from __future__ import annotations + +import importlib +import io +import sys +import types +from collections.abc import Callable +from typing import Any + +import pytest + +from agents.sandbox import Manifest +from agents.sandbox.entries import File +from agents.sandbox.errors import WorkspaceArchiveReadError +from agents.sandbox.manifest import Environment +from agents.sandbox.types import ExecResult + + +def _load_modal_module( + monkeypatch: pytest.MonkeyPatch, +) -> tuple[Any, list[dict[str, object]], list[str]]: + create_calls: list[dict[str, object]] = [] + registry_tags: list[str] = [] + + class _FakeImage: + object_id = "im-123" + + @staticmethod + def from_registry(_tag: str) -> _FakeImage: + registry_tags.append(_tag) + return _FakeImage() + + @staticmethod + def from_id(_image_id: str) -> _FakeImage: + return _FakeImage() + + class _FakeSandboxInstance: + object_id = "sb-123" + + def __init__(self) -> None: + self.terminate_calls = 0 + + def terminate(self) -> None: + self.terminate_calls += 1 + + def poll(self) -> None: + return None + + class _FakeSandbox: + @staticmethod + def create(**kwargs: object) -> _FakeSandboxInstance: + create_calls.append(dict(kwargs)) + return _FakeSandboxInstance() + + @staticmethod + def from_id(_sandbox_id: str) -> _FakeSandboxInstance: + return _FakeSandboxInstance() + + class _FakeApp: + @staticmethod + def lookup(_name: str, *, create_if_missing: bool = False) -> object: + _ = create_if_missing + return object() + + fake_modal: Any = types.ModuleType("modal") + fake_modal.Image = _FakeImage + fake_modal.App = _FakeApp + fake_modal.Sandbox = _FakeSandbox + + fake_container_process: Any = types.ModuleType("modal.container_process") + fake_container_process.ContainerProcess = object + + monkeypatch.setitem(sys.modules, "modal", fake_modal) + monkeypatch.setitem(sys.modules, "modal.container_process", fake_container_process) + sys.modules.pop("agents.extensions.sandbox.sandboxes.modal", None) + + module: Any = importlib.import_module("agents.extensions.sandbox.sandboxes.modal") + return module, create_calls, registry_tags + + +@pytest.mark.asyncio +async def test_modal_sandbox_create_passes_manifest_environment( + monkeypatch: pytest.MonkeyPatch, +) -> None: + modal_module, create_calls, registry_tags = _load_modal_module(monkeypatch) + + client = modal_module.ModalSandboxClient() + session = await client.create( + manifest=Manifest(environment=Environment(value={"SANDBOX_FLAG": "enabled"})), + options=modal_module.ModalSandboxClientOptions(app_name="sandbox-tests"), + ) + + await session._inner._ensure_sandbox() # noqa: SLF001 + + assert create_calls + assert create_calls[0]["env"] == {"SANDBOX_FLAG": "enabled"} + assert registry_tags == ["python:3.11-slim"] + + +@pytest.mark.asyncio +async def test_modal_stop_is_persistence_only_and_shutdown_terminates( + monkeypatch: pytest.MonkeyPatch, +) -> None: + modal_module, _create_calls, _registry_tags = _load_modal_module(monkeypatch) + sandbox = sys.modules["modal"].Sandbox.create() + state = modal_module.ModalSandboxSessionState( + manifest=Manifest(root="/workspace"), + snapshot=modal_module.resolve_snapshot(None, "snapshot"), + app_name="sandbox-tests", + sandbox_id=sandbox.object_id, + ) + session = modal_module.ModalSandboxSession.from_state(state, sandbox=sandbox) + session._running = True + + await session.stop() + + assert sandbox.terminate_calls == 0 + assert session.state.sandbox_id == "sb-123" + assert await session.running() is True + + await session.shutdown() + + assert sandbox.terminate_calls == 1 + assert session.state.sandbox_id is None + assert await session.running() is False + + +@pytest.mark.asyncio +async def test_modal_snapshot_failure_restores_ephemeral_paths( + monkeypatch: pytest.MonkeyPatch, +) -> None: + modal_module, _create_calls, _registry_tags = _load_modal_module(monkeypatch) + + class _FakeRestoreProcess: + def __init__(self, owner: _FakeSnapshotSandbox) -> None: + self._owner = owner + self.stderr = io.BytesIO(b"") + self.stdin = self._FakeStdin(owner) + + class _FakeStdin: + def __init__(self, owner: _FakeSnapshotSandbox) -> None: + self._owner = owner + self._buffer = bytearray() + + def write(self, data: bytes) -> None: + self._buffer.extend(data) + + def write_eof(self) -> None: + return + + def drain(self) -> None: + return + + def wait(self) -> int: + self._owner.restore_payloads.append(bytes(self.stdin._buffer)) + return 0 + + class _FakeSnapshotSandbox: + object_id = "sb-123" + + def __init__(self) -> None: + self.restore_payloads: list[bytes] = [] + + def snapshot_filesystem(self) -> str: + raise RuntimeError("snapshot failed") + + def exec(self, *command: object, **kwargs: object) -> _FakeRestoreProcess: + _ = kwargs + assert command[:3] == ("tar", "xf", "-") + return _FakeRestoreProcess(self) + + sandbox = _FakeSnapshotSandbox() + state = modal_module.ModalSandboxSessionState( + manifest=Manifest( + root="/workspace", + entries={"tmp.txt": File(content=b"ephemeral", ephemeral=True)}, + ), + snapshot=modal_module.resolve_snapshot(None, "snapshot"), + app_name="sandbox-tests", + sandbox_id=sandbox.object_id, + workspace_persistence="snapshot_filesystem", + ) + session = modal_module.ModalSandboxSession.from_state(state, sandbox=sandbox) + + async def _fake_exec( + *command: object, + timeout: float | None = None, + shell: bool | list[str] = True, + user: object | None = None, + ) -> ExecResult: + _ = (timeout, shell, user) + rendered = [str(part) for part in command] + if rendered[:2] == ["sh", "-lc"]: + return ExecResult(stdout=b"ephemeral-backup", stderr=b"", exit_code=0) + if rendered[:3] == ["rm", "-rf", "--"]: + return ExecResult(stdout=b"", stderr=b"", exit_code=0) + raise AssertionError(f"unexpected command: {rendered!r}") + + async def _fake_call_modal( + fn: Callable[..., object], + *args: object, + call_timeout: float | None = None, + **kwargs: object, + ) -> object: + _ = call_timeout + return fn(*args, **kwargs) + + monkeypatch.setattr(session, "exec", _fake_exec) + monkeypatch.setattr(session, "_call_modal", _fake_call_modal) + + with pytest.raises(WorkspaceArchiveReadError) as exc_info: + await session.persist_workspace() + + assert exc_info.value.context["reason"] == "snapshot_filesystem_failed" + assert sandbox.restore_payloads == [b"ephemeral-backup"] diff --git a/tests/test_agent_runner.py b/tests/test_agent_runner.py index 8b07297167..2984dd34bd 100644 --- a/tests/test_agent_runner.py +++ b/tests/test_agent_runner.py @@ -54,6 +54,7 @@ from agents.lifecycle import RunHooks from agents.run import AgentRunner, get_default_agent_runner, set_default_agent_runner from agents.run_config import _default_trace_include_sensitive_data +from agents.run_internal.agent_bindings import bind_public_agent from agents.run_internal.items import ( drop_orphan_function_calls, ensure_input_item_format, @@ -2065,7 +2066,7 @@ async def test_conversation_lock_rewind_skips_when_no_snapshot() -> None: agent = Agent(name="test", model=model) result = await get_new_response( - agent=agent, + bindings=bind_public_agent(agent), system_prompt=None, input=[history_item, new_item], output_schema=None, @@ -2110,7 +2111,7 @@ async def test_get_new_response_uses_agent_retry_settings() -> None: ) result = await get_new_response( - agent=agent, + bindings=bind_public_agent(agent), system_prompt=None, input=[get_text_input_item("hello")], output_schema=None, diff --git a/tests/test_agent_runner_sync.py b/tests/test_agent_runner_sync.py index a570eea284..73906e7e93 100644 --- a/tests/test_agent_runner_sync.py +++ b/tests/test_agent_runner_sync.py @@ -1,6 +1,6 @@ import asyncio from collections.abc import Generator -from typing import Any +from typing import Any, Protocol import pytest @@ -8,10 +8,16 @@ from agents.run import AgentRunner +class _EventLoopPolicy(Protocol): + def get_event_loop(self) -> asyncio.AbstractEventLoop: ... + + def set_event_loop(self, loop: asyncio.AbstractEventLoop | None) -> None: ... + + @pytest.fixture -def fresh_event_loop_policy() -> Generator[asyncio.AbstractEventLoopPolicy, None, None]: +def fresh_event_loop_policy() -> Generator[_EventLoopPolicy, None, None]: policy_before = asyncio.get_event_loop_policy() - new_policy = asyncio.DefaultEventLoopPolicy() + new_policy = type(policy_before)() asyncio.set_event_loop_policy(new_policy) try: yield new_policy diff --git a/tests/test_computer_action.py b/tests/test_computer_action.py index bb6823942d..dd69e87537 100644 --- a/tests/test_computer_action.py +++ b/tests/test_computer_action.py @@ -571,7 +571,7 @@ def on_sc(data: ComputerToolSafetyCheckData) -> bool: ctx = RunContextWrapper(context=None) results = await run_loop.execute_computer_actions( - agent=agent, + public_agent=agent, actions=[run_action], hooks=RunHooks[Any](), context_wrapper=ctx, diff --git a/tests/test_example_workflows.py b/tests/test_example_workflows.py index dff1ef7910..3e2dbcd8a8 100644 --- a/tests/test_example_workflows.py +++ b/tests/test_example_workflows.py @@ -28,6 +28,12 @@ from agents.agent import ToolsToFinalOutputResult from agents.items import TResponseInputItem from agents.tool import FunctionToolResult, function_tool +from examples.sandbox.docker_runner import _stream_event_banner +from examples.sandbox.sandbox_agents_as_tools import ( + PricingPacketReview, + RolloutRiskReview, + _structured_tool_output_extractor, +) from .fake_model import FakeModel from .test_responses import ( @@ -487,6 +493,162 @@ async def fake_invoke(ctx, input: str) -> str: ) +@pytest.mark.asyncio +async def test_sandbox_agents_as_tools_example_serializes_structured_reviews() -> None: + pricing_model = FakeModel() + pricing_model.set_next_output( + [ + get_final_output_message( + json.dumps( + { + "requested_discount_percent": 15, + "requested_term_months": 24, + "pricing_risk": "medium", + "summary": "Discount ask is above target band.", + "recommended_next_step": "Trade discount for a stronger give-get.", + "evidence_files": ["pricing_summary.md", "commercial_notes.md"], + } + ) + ) + ] + ) + rollout_model = FakeModel() + rollout_model.set_next_output( + [ + get_final_output_message( + json.dumps( + { + "rollout_risk": "medium", + "summary": "Launch timing is compressed.", + "blockers": [ + "Regional admin training is incomplete.", + "SSO migration lands in week 2.", + ], + "recommended_next_step": "Require a phased rollout plan.", + "evidence_files": ["rollout_plan.md", "support_history.md"], + } + ) + ) + ] + ) + orchestrator_model = FakeModel() + orchestrator_model.add_multiple_turn_outputs( + [ + [ + get_function_tool_call( + "review_pricing_packet", + json.dumps({"input": "Review pricing"}), + call_id="outer_pricing", + ), + get_function_tool_call( + "review_rollout_risk", + json.dumps({"input": "Review rollout"}), + call_id="outer_rollout", + ), + get_function_tool_call( + "get_discount_approval_rule", + json.dumps({"discount_percent": 15}), + call_id="outer_approval", + ), + ], + [get_text_message("Recommendation complete")], + ] + ) + + @function_tool + def get_discount_approval_rule(discount_percent: int) -> str: + if discount_percent <= 10: + return "AE" + if discount_percent <= 15: + return "RSD" + return "Finance + RSD" + + pricing_agent = Agent( + name="pricing", + model=pricing_model, + output_type=PricingPacketReview, + ) + rollout_agent = Agent( + name="rollout", + model=rollout_model, + output_type=RolloutRiskReview, + ) + orchestrator = Agent( + name="orchestrator", + model=orchestrator_model, + tools=[ + pricing_agent.as_tool( + "review_pricing_packet", + "Pricing review", + custom_output_extractor=_structured_tool_output_extractor, + ), + rollout_agent.as_tool( + "review_rollout_risk", + "Rollout review", + custom_output_extractor=_structured_tool_output_extractor, + ), + get_discount_approval_rule, + ], + model_settings=ModelSettings(tool_choice="required"), + ) + + result = await Runner.run(orchestrator, "Review the renewal") + + assert result.final_output == "Recommendation complete" + outer_second_turn_input = cast( + list[dict[str, Any]], + orchestrator_model.last_turn_args["input"], + ) + outer_tool_outputs = [ + item for item in outer_second_turn_input if item.get("type") == "function_call_output" + ] + assert outer_tool_outputs == [ + { + "call_id": "outer_pricing", + "output": json.dumps( + { + "evidence_files": ["pricing_summary.md", "commercial_notes.md"], + "pricing_risk": "medium", + "recommended_next_step": "Trade discount for a stronger give-get.", + "requested_discount_percent": 15, + "requested_term_months": 24, + "summary": "Discount ask is above target band.", + }, + sort_keys=True, + ), + "type": "function_call_output", + }, + { + "call_id": "outer_rollout", + "output": json.dumps( + { + "blockers": [ + "Regional admin training is incomplete.", + "SSO migration lands in week 2.", + ], + "evidence_files": ["rollout_plan.md", "support_history.md"], + "recommended_next_step": "Require a phased rollout plan.", + "rollout_risk": "medium", + "summary": "Launch timing is compressed.", + }, + sort_keys=True, + ), + "type": "function_call_output", + }, + { + "call_id": "outer_approval", + "output": "RSD", + "type": "function_call_output", + }, + ] + + +def test_docker_runner_stream_event_banner_uses_stable_event_names() -> None: + assert _stream_event_banner("tool_called") == "[tool call] shell" + assert _stream_event_banner("tool_output") == "[tool output] shell" + assert _stream_event_banner("message_output_created") is None + + @pytest.mark.asyncio async def test_forcing_tool_use_behaviors_align_with_example() -> None: """Mimics forcing_tool_use example: default vs first_tool vs custom behaviors.""" diff --git a/tests/test_hitl_error_scenarios.py b/tests/test_hitl_error_scenarios.py index d0de312d69..845147aebe 100644 --- a/tests/test_hitl_error_scenarios.py +++ b/tests/test_hitl_error_scenarios.py @@ -26,6 +26,7 @@ function_tool, tool_namespace, ) +from agents._public_agent import set_public_agent from agents.computer import Computer, Environment from agents.exceptions import ModelBehaviorError, UserError from agents.items import ( @@ -39,10 +40,12 @@ from agents.lifecycle import RunHooks from agents.run import RunConfig from agents.run_internal import run_loop +from agents.run_internal.agent_bindings import bind_execution_agent, bind_public_agent from agents.run_internal.run_loop import ( NextStepInterruption, NextStepRunAgain, ProcessedResponse, + ToolRunApplyPatchCall, ToolRunComputerAction, ToolRunFunction, ToolRunMCPApprovalRequest, @@ -84,6 +87,20 @@ ) +def _bind_agent(agent: Agent[Any]): + public_agent = getattr(agent, "_agents_public_agent", None) + if isinstance(public_agent, Agent): + return bind_execution_agent(public_agent=public_agent, execution_agent=agent) + return bind_public_agent(agent) + + +async def _resolve_interrupted_turn(*, agent: Agent[Any], **kwargs: Any): + return await run_loop.resolve_interrupted_turn( + bindings=_bind_agent(agent), + **kwargs, + ) + + class TrackingComputer(Computer): """Minimal computer implementation that records method calls.""" @@ -705,7 +722,7 @@ class DummyMcpTool: interruptions=[], ) - result = await run_loop.resolve_interrupted_turn( + result = await _resolve_interrupted_turn( agent=agent, original_input="test", original_pre_step_items=[approval_item], @@ -745,7 +762,7 @@ async def test_shell_call_without_call_id_raises() -> None: ) with pytest.raises(ModelBehaviorError): - await run_loop.resolve_interrupted_turn( + await _resolve_interrupted_turn( agent=agent, original_input="test", original_pre_step_items=[], @@ -891,7 +908,7 @@ def bad_tool() -> str: ) with pytest.raises(UserError, match="needs_approval"): - await run_loop.resolve_interrupted_turn( + await _resolve_interrupted_turn( agent=agent, original_input="resume invalid", original_pre_step_items=[], @@ -1006,7 +1023,7 @@ def approve_me(reason: Optional[str] = None) -> str: # noqa: UP007 interruptions=[], ) - result = await run_loop.resolve_interrupted_turn( + result = await _resolve_interrupted_turn( agent=agent, original_input="resume approvals", original_pre_step_items=[], @@ -1078,7 +1095,7 @@ async def deferred_lookup_account(customer_id: str) -> str: interruptions=[], ) - result = await run_loop.resolve_interrupted_turn( + result = await _resolve_interrupted_turn( agent=agent, original_input="resume approvals", original_pre_step_items=[], @@ -1099,6 +1116,71 @@ async def deferred_lookup_account(customer_id: str) -> str: assert deferred_outputs == ["deferred:customer_1"] +@pytest.mark.asyncio +async def test_resume_does_not_rebuild_approved_calls_for_same_named_sibling_agent() -> None: + """Approved interruptions should match the current public agent, not any same-named sibling.""" + + first_calls: list[str] = [] + second_calls: list[str] = [] + + @function_tool(needs_approval=True, name_override="approval_tool") + async def first_approval_tool() -> str: + first_calls.append("first") + return "first" + + @function_tool(needs_approval=True, name_override="approval_tool") + async def second_approval_tool() -> str: + second_calls.append("second") + return "second" + + first = Agent(name="sandbox", tools=[first_approval_tool]) + second = Agent(name="sandbox", tools=[second_approval_tool]) + first.handoffs = [second] + second.handoffs = [first] + + approval_item = ToolApprovalItem( + agent=second, + raw_item=make_function_tool_call( + name="approval_tool", + call_id="call-sibling-approval", + arguments="{}", + ), + tool_name="approval_tool", + ) + context_wrapper = make_context_wrapper() + context_wrapper.approve_tool(approval_item) + run_state = make_state_with_interruptions(first, [approval_item]) + processed_response = ProcessedResponse( + new_items=[], + handoffs=[], + functions=[], + computer_actions=[], + local_shell_calls=[], + shell_calls=[], + apply_patch_calls=[], + tools_used=[], + mcp_approval_requests=[], + interruptions=[], + ) + + execution_agent = set_public_agent(first.clone(), first) + result = await _resolve_interrupted_turn( + agent=execution_agent, + original_input="resume approvals", + original_pre_step_items=[], + new_response=ModelResponse(output=[], usage=Usage(), response_id="resp"), + processed_response=processed_response, + hooks=RunHooks(), + context_wrapper=context_wrapper, + run_config=RunConfig(), + run_state=run_state, + ) + + assert first_calls == [] + assert second_calls == [] + assert not any(isinstance(item, ToolCallOutputItem) for item in result.new_step_items) + + @pytest.mark.asyncio async def test_resume_honors_permanent_namespaced_function_approval_with_new_call_id() -> None: @function_tool(needs_approval=True, name_override="lookup_account") @@ -1198,7 +1280,7 @@ def approve_me(reason: Optional[str] = None) -> str: # noqa: UP007 interruptions=[], ) - result = await run_loop.resolve_interrupted_turn( + result = await _resolve_interrupted_turn( agent=agent, original_input="resume approvals", original_pre_step_items=[], @@ -1252,7 +1334,7 @@ async def test_resume_rebuilds_local_mcp_function_runs_from_approvals() -> None: interruptions=[], ) - result = await run_loop.resolve_interrupted_turn( + result = await _resolve_interrupted_turn( agent=agent, original_input="resume approvals", original_pre_step_items=[], @@ -1317,7 +1399,7 @@ async def get_weather() -> str: interruptions=[], ) - result = await run_loop.resolve_interrupted_turn( + result = await _resolve_interrupted_turn( agent=agent, original_input="resume approvals", original_pre_step_items=[], @@ -1377,7 +1459,7 @@ def pending_me(text: str = "wait") -> str: interruptions=[], ) - result = await run_loop.resolve_interrupted_turn( + result = await _resolve_interrupted_turn( agent=agent, original_input="resume approvals", original_pre_step_items=[], @@ -1399,6 +1481,127 @@ def pending_me(text: str = "wait") -> str: assert rejection_outputs, "Rejected function call should emit rejection output" +@pytest.mark.asyncio +async def test_resume_function_rejection_outputs_use_public_agent() -> None: + @function_tool(needs_approval=True) + def reject_me(text: str = "nope") -> str: + return text + + _model, public_agent = make_model_and_agent(tools=[reject_me]) + execution_agent = public_agent.clone() + set_public_agent(execution_agent, public_agent) + context_wrapper = make_context_wrapper() + + rejected_call = make_function_tool_call(reject_me.name, call_id="obj-reject-public") + assert isinstance(rejected_call, ResponseFunctionToolCall) + rejected_item = ToolApprovalItem(agent=public_agent, raw_item=rejected_call) + context_wrapper.reject_tool(rejected_item) + + run_state = make_state_with_interruptions(public_agent, [rejected_item]) + processed_response = ProcessedResponse( + new_items=[], + handoffs=[], + functions=[], + computer_actions=[], + local_shell_calls=[], + shell_calls=[], + apply_patch_calls=[], + tools_used=[], + mcp_approval_requests=[], + interruptions=[], + ) + + result = await _resolve_interrupted_turn( + agent=execution_agent, + original_input="resume approvals", + original_pre_step_items=[], + new_response=ModelResponse(output=[], usage=Usage(), response_id="resp"), + processed_response=processed_response, + hooks=RunHooks(), + context_wrapper=context_wrapper, + run_config=RunConfig(), + run_state=run_state, + ) + + rejection_outputs = [ + item + for item in result.new_step_items + if isinstance(item, ToolCallOutputItem) and item.output == HITL_REJECTION_MSG + ] + assert rejection_outputs + assert all(item.agent is public_agent for item in rejection_outputs) + + +@pytest.mark.parametrize("tool_kind", ["shell", "apply_patch"]) +@pytest.mark.asyncio +async def test_resume_non_function_rejection_outputs_use_public_agent( + tool_kind: str, +) -> None: + context_wrapper = make_context_wrapper() + processed_response = ProcessedResponse( + new_items=[], + handoffs=[], + functions=[], + computer_actions=[], + local_shell_calls=[], + shell_calls=[], + apply_patch_calls=[], + tools_used=[], + mcp_approval_requests=[], + interruptions=[], + ) + + if tool_kind == "shell": + shell_tool = ShellTool(executor=lambda _req: "should_not_run", needs_approval=True) + _model, public_agent = make_model_and_agent(tools=[shell_tool]) + raw_item = cast( + dict[str, Any], + make_shell_call( + "call_reject_shell_public", + id_value="shell_reject_public", + commands=["echo test"], + status="in_progress", + ), + ) + processed_response.shell_calls = [ + ToolRunShellCall(tool_call=raw_item, shell_tool=shell_tool) + ] + tool_name = shell_tool.name + else: + apply_patch_tool = ApplyPatchTool(editor=RecordingEditor(), needs_approval=True) + _model, public_agent = make_model_and_agent(tools=[apply_patch_tool]) + raw_item = cast(Any, make_apply_patch_dict("call_apply_reject_public")) + processed_response.apply_patch_calls = [ + ToolRunApplyPatchCall(tool_call=raw_item, apply_patch_tool=apply_patch_tool) + ] + tool_name = apply_patch_tool.name + + execution_agent = public_agent.clone() + set_public_agent(execution_agent, public_agent) + approval_item = ToolApprovalItem(agent=public_agent, raw_item=raw_item, tool_name=tool_name) + context_wrapper.reject_tool(approval_item) + + result = await _resolve_interrupted_turn( + agent=execution_agent, + original_input="resume rejection", + original_pre_step_items=[], + new_response=ModelResponse(output=[], usage=Usage(), response_id="resp"), + processed_response=processed_response, + hooks=RunHooks(), + context_wrapper=context_wrapper, + run_config=RunConfig(), + run_state=make_state_with_interruptions(public_agent, [approval_item]), + ) + + rejection_outputs = [ + item + for item in result.new_step_items + if isinstance(item, ToolCallOutputItem) and item.output == HITL_REJECTION_MSG + ] + assert rejection_outputs + assert all(item.agent is public_agent for item in rejection_outputs) + + @pytest.mark.asyncio async def test_resume_keeps_unmatched_pending_approvals_with_function_runs() -> None: """Pending approvals should persist even when resume has other function runs.""" @@ -1437,7 +1640,7 @@ def inner_tool() -> str: interruptions=[], ) - result = await run_loop.resolve_interrupted_turn( + result = await _resolve_interrupted_turn( agent=agent, original_input="resume approvals", original_pre_step_items=[], @@ -1477,7 +1680,7 @@ def already_ran() -> str: interruptions=[], ) - result = await run_loop.resolve_interrupted_turn( + result = await _resolve_interrupted_turn( agent=agent, original_input="resume run", original_pre_step_items=[], @@ -1538,7 +1741,7 @@ def already_ran() -> str: ) ] - result = await run_loop.resolve_interrupted_turn( + result = await _resolve_interrupted_turn( agent=agent, original_input="resume run", original_pre_step_items=original_pre_step_items, @@ -1593,7 +1796,7 @@ async def test_resume_skips_shell_calls_with_existing_output() -> None: ) ] - result = await run_loop.resolve_interrupted_turn( + result = await _resolve_interrupted_turn( agent=agent, original_input="resume shell", original_pre_step_items=cast(list[RunItem], original_pre_step_items), @@ -1653,7 +1856,7 @@ def pending_tool() -> str: interruptions=[], ) - result = await run_loop.resolve_interrupted_turn( + result = await _resolve_interrupted_turn( agent=agent, original_input="resume shell with pending approval", original_pre_step_items=[], @@ -1709,7 +1912,7 @@ async def test_resume_executes_pending_computer_actions() -> None: interruptions=[], ) - result = await run_loop.resolve_interrupted_turn( + result = await _resolve_interrupted_turn( agent=agent, original_input="resume computer", original_pre_step_items=[], @@ -1777,7 +1980,7 @@ async def test_resume_skips_computer_actions_with_existing_output() -> None: ) ] - result = await run_loop.resolve_interrupted_turn( + result = await _resolve_interrupted_turn( agent=agent, original_input="resume computer existing", original_pre_step_items=cast(list[RunItem], original_pre_step_items), @@ -1840,7 +2043,7 @@ def pending_me(text: str = "wait") -> str: interruptions=[], ) - result = await run_loop.resolve_interrupted_turn( + result = await _resolve_interrupted_turn( agent=agent, original_input="resume approvals", original_pre_step_items=[], @@ -1910,7 +2113,7 @@ async def test_rebuild_preserves_unmatched_pending_approvals( interruptions=[], ) - result = await run_loop.resolve_interrupted_turn( + result = await _resolve_interrupted_turn( agent=agent, original_input="resume approvals", original_pre_step_items=[], @@ -1957,7 +2160,7 @@ async def test_rejected_shell_calls_emit_rejection_output() -> None: interruptions=[], ) - result = await run_loop.resolve_interrupted_turn( + result = await _resolve_interrupted_turn( agent=agent, original_input="resume shell rejection", original_pre_step_items=[], @@ -2041,7 +2244,7 @@ async def test_rejected_shell_calls_with_existing_output_are_not_duplicated() -> ) ] - result = await run_loop.resolve_interrupted_turn( + result = await _resolve_interrupted_turn( agent=agent, original_input="resume shell rejection existing", original_pre_step_items=cast(list[RunItem], original_pre_step_items), @@ -2101,7 +2304,7 @@ def __init__(self) -> None: interruptions=[], ) - result = await run_loop.resolve_interrupted_turn( + result = await _resolve_interrupted_turn( agent=agent, original_input="handle mcp", original_pre_step_items=[], diff --git a/tests/test_run_impl_resume_paths.py b/tests/test_run_impl_resume_paths.py index 542d1f3749..07ffbf97c1 100644 --- a/tests/test_run_impl_resume_paths.py +++ b/tests/test_run_impl_resume_paths.py @@ -1,5 +1,5 @@ import json -from typing import cast +from typing import Any, cast import pytest from openai.types.responses import ResponseFunctionToolCall, ResponseOutputMessage @@ -7,11 +7,18 @@ import agents.run as run_module from agents import Agent, Runner, function_tool from agents.agent import ToolsToFinalOutputResult -from agents.items import MessageOutputItem, ModelResponse, ToolCallItem, ToolCallOutputItem +from agents.items import ( + MessageOutputItem, + ModelResponse, + ToolApprovalItem, + ToolCallItem, + ToolCallOutputItem, +) from agents.lifecycle import RunHooks from agents.run import RunConfig from agents.run_context import RunContextWrapper from agents.run_internal import run_loop, turn_resolution +from agents.run_internal.agent_bindings import bind_public_agent from agents.run_internal.run_loop import ( NextStepFinalOutput, NextStepInterruption, @@ -84,7 +91,7 @@ async def fake_execute_final_output( ) result = await run_loop.resolve_interrupted_turn( - agent=agent, + bindings=bind_public_agent(agent), original_input="input", original_pre_step_items=[], new_response=ModelResponse(output=[], usage=Usage(), response_id="resp"), @@ -266,3 +273,110 @@ async def test_tool() -> str: assert call_count == 1 assert output_count == 1 + + +@pytest.mark.asyncio +@pytest.mark.parametrize( + ("schema_version", "expect_execution"), + [("1.6", True), ("1.7", False)], +) +async def test_resolve_interrupted_turn_only_uses_name_fallback_for_legacy_approval_agents( + schema_version: str, + expect_execution: bool, +) -> None: + calls: list[str] = [] + + @function_tool(name_override="needs_ok", needs_approval=True) + async def needs_ok(text: str) -> str: + calls.append(text) + return text + + base_duplicate = Agent(name="duplicate", instructions="alpha", tools=[needs_ok]) + resumed_duplicate = Agent(name="duplicate", instructions="zeta", tools=[needs_ok]) + root = Agent(name="triage", handoffs=[base_duplicate, resumed_duplicate]) + base_duplicate.handoffs = [root] + resumed_duplicate.handoffs = [root] + + state: RunState[dict[str, str], Agent[Any]] = RunState( + context=RunContextWrapper(context={}), + original_input="input", + starting_agent=root, + max_turns=2, + ) + state._current_agent = resumed_duplicate + state._current_step = NextStepInterruption( + interruptions=[ + ToolApprovalItem( + agent=resumed_duplicate, + raw_item=cast( + ResponseFunctionToolCall, + get_function_tool_call( + "needs_ok", + json.dumps({"text": "one"}), + call_id="legacy-call", + ), + ), + ) + ] + ) + state._last_processed_response = ProcessedResponse( + new_items=[], + handoffs=[], + functions=[], + computer_actions=[], + local_shell_calls=[], + shell_calls=[], + apply_patch_calls=[], + tools_used=[], + mcp_approval_requests=[], + interruptions=[], + ) + state._model_responses = [ModelResponse(output=[], usage=Usage(), response_id="resp")] + + json_data = state.to_json() + current_agent_data = cast(dict[str, str], json_data["current_agent"]) + assert current_agent_data["name"] == "duplicate" + assert "identity" in current_agent_data + + interruption_data = cast( + dict[str, object], + json_data["current_step"]["data"]["interruptions"][0], + ) + interruption_agent_data = cast(dict[str, str], interruption_data["agent"]) + assert interruption_agent_data["identity"] == current_agent_data["identity"] + interruption_agent_data.pop("identity") + json_data["$schemaVersion"] = schema_version + + restored = await RunState.from_json(root, json_data) + assert restored._schema_version == schema_version + assert restored._current_agent is resumed_duplicate + restored_approval = restored.get_interruptions()[0] + restored.approve(restored_approval) + assert restored._context is not None + assert restored._last_processed_response is not None + + result = await turn_resolution.resolve_interrupted_turn( + bindings=bind_public_agent(cast(Agent[dict[str, str]], restored._current_agent)), + original_input=restored._original_input, + original_pre_step_items=restored._generated_items, + new_response=restored._model_responses[-1], + processed_response=restored._last_processed_response, + hooks=RunHooks(), + context_wrapper=restored._context, + run_config=RunConfig(), + run_state=restored, + ) + + if expect_execution: + assert isinstance(result.next_step, NextStepRunAgain) + assert calls == ["one"] + assert any( + isinstance(item, ToolCallOutputItem) and item.output == "one" + for item in result.new_step_items + ) + else: + assert calls == [] + assert not any( + isinstance(item, ToolCallOutputItem) and item.output == "one" + for item in result.new_step_items + ) diff --git a/tests/test_run_state.py b/tests/test_run_state.py index 56cd61fab2..3302feb2d5 100644 --- a/tests/test_run_state.py +++ b/tests/test_run_state.py @@ -68,8 +68,10 @@ ) from agents.run_state import ( CURRENT_SCHEMA_VERSION, + SCHEMA_VERSION_SUMMARIES, SUPPORTED_SCHEMA_VERSIONS, RunState, + _build_agent_identity_map, _build_agent_map, _deserialize_items, _deserialize_processed_response, @@ -101,6 +103,7 @@ from .test_responses import ( get_final_output_message, get_function_tool_call, + get_handoff_tool_call, get_text_message, ) from .utils.factories import ( @@ -118,6 +121,9 @@ run_and_resume_with_mutation, ) +_CURRENT_SCHEMA_MAJOR, _CURRENT_SCHEMA_MINOR = CURRENT_SCHEMA_VERSION.split(".") +_NEXT_UNSUPPORTED_SCHEMA_VERSION = f"{_CURRENT_SCHEMA_MAJOR}.{int(_CURRENT_SCHEMA_MINOR) + 1}" + TContext = TypeVar("TContext") @@ -242,6 +248,304 @@ def test_to_json_and_to_string_produce_valid_json(self): assert isinstance(str_data, str) assert json.loads(str_data) == json_data + @pytest.mark.asyncio + async def test_from_json_restores_duplicate_name_current_agent_by_identity(self): + """Duplicate agent names should round-trip through the serialized identity key.""" + context: RunContextWrapper[dict[str, str]] = RunContextWrapper(context={}) + second = Agent(name="duplicate") + first = Agent(name="duplicate", handoffs=[second]) + second.handoffs = [first] + state = make_state(first, context=context, original_input="input1", max_turns=2) + state._current_agent = second + + json_data = state.to_json() + assert json_data["current_agent"] == {"name": "duplicate", "identity": "duplicate#2"} + + restored = await RunState.from_json(first, json_data) + assert restored._current_agent is second + + def test_build_agent_identity_map_avoids_literal_suffix_collisions(self) -> None: + """Literal `#` names should not collide with generated duplicate identities.""" + first = Agent(name="sandbox") + literal_suffix = Agent(name="sandbox#2") + second = Agent(name="sandbox") + first.handoffs = [literal_suffix, second] + literal_suffix.handoffs = [first, second] + second.handoffs = [first, literal_suffix] + + identity_map = _build_agent_identity_map(first) + + assert identity_map == { + "sandbox": first, + "sandbox#2": literal_suffix, + "sandbox#3": second, + } + + def test_build_agent_identity_map_is_stable_across_reordered_duplicate_agents(self) -> None: + """Duplicate-name identities should not change when reachable order changes.""" + + @function_tool(name_override="alpha_tool") + def alpha_tool() -> str: + return "alpha" + + @function_tool(name_override="beta_tool") + def beta_tool() -> str: + return "beta" + + def _identity_for( + identity_map: Mapping[str, Agent[Any]], + target: Agent[Any], + ) -> str: + return next(identity for identity, agent in identity_map.items() if agent is target) + + first_alpha = Agent(name="sandbox", instructions="Alpha", tools=[alpha_tool]) + first_beta = Agent(name="sandbox", instructions="Beta", tools=[beta_tool]) + first_root = Agent(name="triage", handoffs=[first_beta, first_alpha]) + first_alpha.handoffs = [first_root] + first_beta.handoffs = [first_root] + + second_alpha = Agent(name="sandbox", instructions="Alpha", tools=[alpha_tool]) + second_beta = Agent(name="sandbox", instructions="Beta", tools=[beta_tool]) + second_root = Agent(name="triage", handoffs=[second_alpha, second_beta]) + second_alpha.handoffs = [second_root] + second_beta.handoffs = [second_root] + + first_identity_map = _build_agent_identity_map(first_root) + second_identity_map = _build_agent_identity_map(second_root) + + assert _identity_for(first_identity_map, first_alpha) == _identity_for( + second_identity_map, second_alpha + ) + assert _identity_for(first_identity_map, first_beta) == _identity_for( + second_identity_map, second_beta + ) + + @pytest.mark.asyncio + async def test_from_json_restores_duplicate_name_current_agent_with_reordered_graph(self): + """Restore should keep the same logical duplicate agent after graph reordering.""" + + @function_tool(name_override="alpha_tool") + def alpha_tool() -> str: + return "alpha" + + @function_tool(name_override="beta_tool") + def beta_tool() -> str: + return "beta" + + context: RunContextWrapper[dict[str, str]] = RunContextWrapper(context={}) + first_alpha = Agent(name="sandbox", instructions="Alpha", tools=[alpha_tool]) + first_beta = Agent(name="sandbox", instructions="Beta", tools=[beta_tool]) + first_root = Agent(name="triage", handoffs=[first_beta, first_alpha]) + first_alpha.handoffs = [first_root] + first_beta.handoffs = [first_root] + + state = make_state(first_root, context=context, original_input="input1", max_turns=2) + state._current_agent = first_beta + json_data = state.to_json() + + restored_alpha = Agent(name="sandbox", instructions="Alpha", tools=[alpha_tool]) + restored_beta = Agent(name="sandbox", instructions="Beta", tools=[beta_tool]) + restored_root = Agent(name="triage", handoffs=[restored_alpha, restored_beta]) + restored_alpha.handoffs = [restored_root] + restored_beta.handoffs = [restored_root] + + restored = await RunState.from_json(restored_root, json_data) + assert restored._current_agent is restored_beta + + @pytest.mark.asyncio + async def test_from_json_restores_bare_duplicate_name_current_agent_via_identity_map(self): + """Bare duplicate names should resolve through the identity map, not traversal order.""" + context: RunContextWrapper[dict[str, str]] = RunContextWrapper(context={}) + first = Agent(name="duplicate", instructions="zeta") + second = Agent(name="duplicate", instructions="alpha") + root = Agent(name="triage", handoffs=[first, second]) + first.handoffs = [root] + second.handoffs = [root] + + state = make_state(root, context=context, original_input="input1", max_turns=2) + state._current_agent = second + + json_data = state.to_json() + assert json_data["current_agent"] == {"name": "duplicate"} + + restored = await RunState.from_json(root, json_data) + assert restored._current_agent is second + + def test_build_agent_identity_map_uses_tool_use_behavior_for_duplicate_names(self) -> None: + """Duplicate-name identities should stay stable when only tool_use_behavior differs.""" + + def _identity_for( + identity_map: Mapping[str, Agent[Any]], + target: Agent[Any], + ) -> str: + return next(identity for identity, agent in identity_map.items() if agent is target) + + first_default = Agent( + name="sandbox", + instructions="Shared instructions.", + tool_use_behavior="run_llm_again", + ) + first_stop = Agent( + name="sandbox", + instructions="Shared instructions.", + tool_use_behavior="stop_on_first_tool", + ) + first_root = Agent(name="triage", handoffs=[first_default, first_stop]) + first_default.handoffs = [first_root] + first_stop.handoffs = [first_root] + + second_default = Agent( + name="sandbox", + instructions="Shared instructions.", + tool_use_behavior="run_llm_again", + ) + second_stop = Agent( + name="sandbox", + instructions="Shared instructions.", + tool_use_behavior="stop_on_first_tool", + ) + second_root = Agent(name="triage", handoffs=[second_stop, second_default]) + second_default.handoffs = [second_root] + second_stop.handoffs = [second_root] + + first_identity_map = _build_agent_identity_map(first_root) + second_identity_map = _build_agent_identity_map(second_root) + + assert _identity_for(first_identity_map, first_default) == _identity_for( + second_identity_map, second_default + ) + assert _identity_for(first_identity_map, first_stop) == _identity_for( + second_identity_map, second_stop + ) + + @pytest.mark.asyncio + async def test_from_json_restores_duplicate_name_current_agent_when_tool_use_behavior_differs( + self, + ) -> None: + """Duplicate-name restore should stay stable when tool_use_behavior is the only delta.""" + context: RunContextWrapper[dict[str, str]] = RunContextWrapper(context={}) + first_default = Agent( + name="sandbox", + instructions="Shared instructions.", + tool_use_behavior="run_llm_again", + ) + first_stop = Agent( + name="sandbox", + instructions="Shared instructions.", + tool_use_behavior="stop_on_first_tool", + ) + first_root = Agent(name="triage", handoffs=[first_default, first_stop]) + first_default.handoffs = [first_root] + first_stop.handoffs = [first_root] + + state = make_state(first_root, context=context, original_input="input1", max_turns=2) + state._current_agent = first_stop + json_data = state.to_json() + + restored_default = Agent( + name="sandbox", + instructions="Shared instructions.", + tool_use_behavior="run_llm_again", + ) + restored_stop = Agent( + name="sandbox", + instructions="Shared instructions.", + tool_use_behavior="stop_on_first_tool", + ) + restored_root = Agent(name="triage", handoffs=[restored_stop, restored_default]) + restored_default.handoffs = [restored_root] + restored_stop.handoffs = [restored_root] + + restored = await RunState.from_json(restored_root, json_data) + assert restored._current_agent is restored_stop + + @pytest.mark.asyncio + async def test_from_json_rejects_missing_saved_duplicate_identity(self): + """Identity-aware snapshots should fail when the saved duplicate no longer exists.""" + context: RunContextWrapper[dict[str, str]] = RunContextWrapper(context={}) + second = Agent(name="duplicate", instructions="Second") + first = Agent(name="duplicate", instructions="First", handoffs=[second]) + second.handoffs = [first] + state = make_state(first, context=context, original_input="input1", max_turns=2) + state._current_agent = second + + json_data = state.to_json() + restored_root = Agent(name="duplicate", instructions="First") + + with pytest.raises(UserError, match="agent identity"): + await RunState.from_json(restored_root, json_data) + + @pytest.mark.asyncio + async def test_result_to_state_preserves_duplicate_name_root_and_owned_state(self): + """RunResult.to_state should keep the root graph while preserving the active duplicate.""" + + @function_tool(name_override="approval_tool", needs_approval=True) + def approval_tool() -> str: + return "approved" + + first_model = FakeModel() + second_model = FakeModel() + first = Agent(name="duplicate", model=first_model) + second = Agent( + name="duplicate", + model=second_model, + tools=[approval_tool], + model_settings=ModelSettings(tool_choice="required"), + ) + first.handoffs = [second] + second.handoffs = [first] + + first_model.add_multiple_turn_outputs([[get_handoff_tool_call(second)]]) + second_model.add_multiple_turn_outputs( + [[get_function_tool_call("approval_tool", json.dumps({}), call_id="call_approval")]] + ) + + result = await Runner.run(first, "start") + assert result.interruptions + + state = result.to_state() + assert state._starting_agent is first + assert state._current_agent is second + + json_data = state.to_json() + assert json_data["current_agent"] == {"name": "duplicate", "identity": "duplicate#2"} + assert json_data["tool_use_tracker"]["duplicate#2"] == ["approval_tool"] + assert json_data["current_step"] is not None + assert json_data["current_step"]["data"]["interruptions"][0]["agent"] == { + "name": "duplicate", + "identity": "duplicate#2", + } + + approval_tool_items = [ + item + for item in json_data["generated_items"] + if item["type"] == "tool_call_item" + and item["raw_item"].get("call_id") == "call_approval" + ] + assert len(approval_tool_items) == 1 + assert approval_tool_items[0]["agent"] == { + "name": "duplicate", + "identity": "duplicate#2", + } + assert approval_tool_items[0]["raw_item"] == { + "arguments": "{}", + "call_id": "call_approval", + "id": "1", + "name": "approval_tool", + "type": "function_call", + } + + restored = await RunState.from_json(first, json_data) + assert restored._starting_agent is first + assert restored._current_agent is second + assert restored.get_interruptions()[0].agent is second + assert any( + isinstance(item, ToolCallItem) + and item.agent is second + and getattr(item.raw_item, "call_id", None) == "call_approval" + for item in restored._generated_items + ) + async def test_reasoning_item_id_policy_survives_serialization(self): """RunState should preserve reasoning item input policy across serialization.""" context: RunContextWrapper[dict[str, str]] = RunContextWrapper(context={}) @@ -1917,6 +2221,64 @@ async def test_serialization_includes_handoff_fields(self): assert len(restored._generated_items) == 1 assert restored._generated_items[0].type == "handoff_output_item" + @pytest.mark.asyncio + async def test_serialization_uses_duplicate_identities_for_handoff_and_output_guardrails(self): + """Duplicate-name item ownership should round-trip with identity keys.""" + first = Agent(name="duplicate") + second = Agent(name="duplicate") + third = Agent(name="duplicate") + first.handoffs = [second, third] + second.handoffs = [third] + third.handoffs = [first] + + context: RunContextWrapper[dict[str, str]] = RunContextWrapper(context={}) + state = make_state(first, context=context, original_input="test handoff", max_turns=2) + state._current_agent = second + state._generated_items = [ + HandoffOutputItem( + agent=second, + raw_item={"type": "handoff_output", "status": "completed"}, # type: ignore[arg-type] + source_agent=second, + target_agent=third, + ) + ] + + output_guardrail = OutputGuardrail( + guardrail_function=lambda _ctx, _agent, _output: GuardrailFunctionOutput( + output_info={"guardrail": "ok"}, + tripwire_triggered=False, + ), + name="duplicate_output_guardrail", + ) + state._output_guardrail_results = [ + OutputGuardrailResult( + guardrail=output_guardrail, + agent_output="done", + agent=third, + output=GuardrailFunctionOutput( + output_info={"guardrail": "ok"}, + tripwire_triggered=False, + ), + ) + ] + + json_data = state.to_json() + item_data = json_data["generated_items"][0] + assert item_data["agent"] == {"name": "duplicate", "identity": "duplicate#2"} + assert item_data["source_agent"] == {"name": "duplicate", "identity": "duplicate#2"} + assert item_data["target_agent"] == {"name": "duplicate", "identity": "duplicate#3"} + assert json_data["output_guardrail_results"][0]["agent"] == { + "name": "duplicate", + "identity": "duplicate#3", + } + + restored = await RunState.from_json(first, json_data) + restored_item = cast(HandoffOutputItem, restored._generated_items[0]) + assert restored_item.agent is second + assert restored_item.source_agent is second + assert restored_item.target_agent is third + assert restored._output_guardrail_results[0].agent is third + async def test_model_response_serialization_roundtrip(self): """Test that model responses serialize and deserialize correctly.""" @@ -3969,7 +4331,7 @@ async def test_from_json_missing_schema_version(self): await RunState.from_json(agent, state_json) @pytest.mark.asyncio - @pytest.mark.parametrize("schema_version", ["1.7", "2.0"]) + @pytest.mark.parametrize("schema_version", [_NEXT_UNSUPPORTED_SCHEMA_VERSION, "2.0", "9.9"]) async def test_from_json_unsupported_schema_version(self, schema_version: str): """Test that from_json raises error when schema version is unsupported.""" agent = Agent(name="TestAgent") @@ -4021,9 +4383,42 @@ async def test_from_json_accepts_previous_schema_version(self): def test_supported_schema_versions_match_released_boundary(self): """The support set should include released versions plus the current unreleased writer.""" assert SUPPORTED_SCHEMA_VERSIONS == frozenset( - {"1.0", "1.1", "1.2", "1.3", "1.4", "1.5", CURRENT_SCHEMA_VERSION} + {"1.0", "1.1", "1.2", "1.3", "1.4", "1.5", "1.6", CURRENT_SCHEMA_VERSION} ) + def test_supported_schema_versions_have_non_empty_summaries(self): + """Every supported schema version should have a one-line historical summary.""" + assert frozenset(SCHEMA_VERSION_SUMMARIES) == SUPPORTED_SCHEMA_VERSIONS + assert CURRENT_SCHEMA_VERSION in SCHEMA_VERSION_SUMMARIES + assert all(summary.strip() for summary in SCHEMA_VERSION_SUMMARIES.values()) + + @pytest.mark.asyncio + async def test_from_json_accepts_schema_version_1_5_without_sandbox_payload(self): + """RunState snapshots written before sandbox resume support should still restore.""" + agent = Agent(name="TestAgent") + state_json = { + "$schemaVersion": "1.5", + "original_input": "test", + "current_agent": {"name": "TestAgent"}, + "context": { + "context": {"foo": "bar"}, + "usage": {"requests": 0, "input_tokens": 0, "output_tokens": 0, "total_tokens": 0}, + "approvals": {}, + }, + "max_turns": 3, + "current_turn": 0, + "model_responses": [], + "generated_items": [], + } + + restored = await RunState.from_json(agent, state_json) + + assert restored._current_agent is not None + assert restored._current_agent.name == "TestAgent" + assert restored._context is not None + assert restored._context.context == {"foo": "bar"} + assert restored._sandbox is None + @pytest.mark.asyncio async def test_from_json_agent_not_found(self): """Test that from_json raises error when agent is not found in agent map.""" diff --git a/tests/test_run_step_execution.py b/tests/test_run_step_execution.py index c8226903a8..4cb8fa7718 100644 --- a/tests/test_run_step_execution.py +++ b/tests/test_run_step_execution.py @@ -46,7 +46,9 @@ tool_output_guardrail, trace, ) -from agents.run_internal import run_loop +from agents._public_agent import set_public_agent +from agents.run_internal import run_loop, turn_resolution +from agents.run_internal.agent_bindings import bind_execution_agent, bind_public_agent from agents.run_internal.run_loop import ( NextStepFinalOutput, NextStepHandoff, @@ -106,6 +108,13 @@ def _function_span_names() -> list[str]: return names +def _bind_agent(agent: Agent[Any]): + public_agent = getattr(agent, "_agents_public_agent", None) + if isinstance(public_agent, Agent): + return bind_execution_agent(public_agent=public_agent, execution_agent=agent) + return bind_public_agent(agent) + + @pytest.mark.asyncio async def test_empty_response_is_final_output(): agent = Agent[None](name="test") @@ -1142,7 +1151,7 @@ def _failure_handler(_ctx: RunContextWrapper[Any], error: Exception) -> str: execution_task = asyncio.create_task( execute_function_tool_calls( - agent=agent, + bindings=bind_public_agent(agent), tool_runs=tool_runs, hooks=RecordingHooks(), context_wrapper=RunContextWrapper(None), @@ -1188,7 +1197,7 @@ async def _shipping_eta(tracking_number: str) -> str: with trace("test_execute_function_tool_calls_collapse_trace_name_for_top_level_deferred_tools"): await execute_function_tool_calls( - agent=Agent(name="test", tools=[tool]), + bindings=bind_public_agent(Agent(name="test", tools=[tool])), tool_runs=[tool_run], hooks=RunHooks(), context_wrapper=RunContextWrapper(None), @@ -1230,7 +1239,7 @@ async def _shipping_eta(tracking_number: str) -> str: with trace("test_execute_function_tool_calls_preserve_trace_name_for_explicit_namespace"): await execute_function_tool_calls( - agent=Agent(name="test", tools=[tool]), + bindings=bind_public_agent(Agent(name="test", tools=[tool])), tool_runs=[tool_run], hooks=RunHooks(), context_wrapper=RunContextWrapper(None), @@ -2556,7 +2565,7 @@ async def get_execute_result( handoffs=handoffs, ) return await run_loop.execute_tools_and_side_effects( - agent=agent, + bindings=_bind_agent(agent), original_input=original_input or "hello", new_response=response, pre_step_items=generated_items or [], @@ -2574,7 +2583,7 @@ async def run_execute_with_processed_response( """Execute tools for a pre-constructed ProcessedResponse.""" return await run_loop.execute_tools_and_side_effects( - agent=agent, + bindings=_bind_agent(agent), original_input="test", pre_step_items=[], new_response=ModelResponse(output=[], usage=Usage(), response_id="resp"), @@ -2759,6 +2768,58 @@ async def test_execute_tools_runs_hosted_mcp_callback_when_present(): assert not result.processed_response or not result.processed_response.interruptions +@pytest.mark.asyncio +async def test_execute_tools_uses_public_agent_for_hosted_mcp_callback_results(): + """Hosted MCP callback responses should expose the public agent when execution uses a clone.""" + + mcp_tool = HostedMCPTool( + tool_config={ + "type": "mcp", + "server_label": "test_mcp_server", + "server_url": "https://example.com", + "require_approval": "always", + }, + on_approval_request=lambda request: {"approve": True}, + ) + public_agent = make_agent(tools=[mcp_tool]) + execution_agent = public_agent.clone() + set_public_agent(execution_agent, public_agent) + request_item = McpApprovalRequest( + id="mcp-approval-callback-public-agent", + type="mcp_approval_request", + server_label="test_mcp_server", + arguments="{}", + name="list_repo_languages", + ) + processed_response = make_processed_response( + new_items=[MCPApprovalRequestItem(raw_item=request_item, agent=execution_agent)], + mcp_approval_requests=[ + ToolRunMCPApprovalRequest( + request_item=request_item, + mcp_tool=mcp_tool, + ) + ], + ) + + result = await run_loop.execute_tools_and_side_effects( + bindings=_bind_agent(execution_agent), + original_input="test", + pre_step_items=[], + new_response=ModelResponse(output=[], usage=Usage(), response_id="resp"), + processed_response=processed_response, + output_schema=None, + hooks=RunHooks(), + context_wrapper=make_context_wrapper(), + run_config=RunConfig(), + ) + + assert not isinstance(result.next_step, NextStepInterruption) + assert any( + isinstance(item, MCPApprovalResponseItem) and item.agent is public_agent + for item in result.new_step_items + ) + + @pytest.mark.asyncio async def test_execute_tools_surfaces_hosted_mcp_interruptions_without_callback(): """Hosted MCP approvals should surface as interruptions when no callback is provided.""" @@ -2802,6 +2863,150 @@ async def test_execute_tools_surfaces_hosted_mcp_interruptions_without_callback( ) +@pytest.mark.asyncio +async def test_execute_tools_uses_public_agent_for_hosted_mcp_interruptions(): + """Hosted MCP approval items should expose the public agent when execution uses a clone.""" + + mcp_tool = HostedMCPTool( + tool_config={ + "type": "mcp", + "server_label": "test_mcp_server", + "server_url": "https://example.com", + "require_approval": "always", + }, + on_approval_request=None, + ) + public_agent = make_agent(tools=[mcp_tool]) + execution_agent = public_agent.clone() + set_public_agent(execution_agent, public_agent) + request_item = McpApprovalRequest( + id="mcp-approval-public-agent", + type="mcp_approval_request", + server_label="test_mcp_server", + arguments="{}", + name="list_repo_languages", + ) + processed_response = make_processed_response( + new_items=[MCPApprovalRequestItem(raw_item=request_item, agent=execution_agent)], + mcp_approval_requests=[ + ToolRunMCPApprovalRequest( + request_item=request_item, + mcp_tool=mcp_tool, + ) + ], + ) + + result = await run_loop.execute_tools_and_side_effects( + bindings=_bind_agent(execution_agent), + original_input="test", + pre_step_items=[], + new_response=ModelResponse(output=[], usage=Usage(), response_id="resp"), + processed_response=processed_response, + output_schema=None, + hooks=RunHooks(), + context_wrapper=make_context_wrapper(), + run_config=RunConfig(), + ) + + assert isinstance(result.next_step, NextStepInterruption) + assert result.next_step.interruptions + assert all(item.agent is public_agent for item in result.next_step.interruptions) + assert any( + isinstance(item, ToolApprovalItem) + and getattr(item.raw_item, "id", None) == "mcp-approval-public-agent" + and item.agent is public_agent + for item in result.new_step_items + ) + + +@pytest.mark.asyncio +async def test_resolve_interrupted_turn_uses_public_agent_for_resumed_hosted_mcp_approvals(): + """Resumed hosted MCP approvals should keep the public agent on approval responses.""" + + mcp_tool = HostedMCPTool( + tool_config={ + "type": "mcp", + "server_label": "test_mcp_server", + "server_url": "https://example.com", + "require_approval": "always", + }, + on_approval_request=None, + ) + public_agent = make_agent(tools=[mcp_tool]) + execution_agent = public_agent.clone() + set_public_agent(execution_agent, public_agent) + request_item = McpApprovalRequest( + id="mcp-approval-resume-public-agent", + type="mcp_approval_request", + server_label="test_mcp_server", + arguments="{}", + name="list_repo_languages", + ) + approval_item = ToolApprovalItem( + agent=public_agent, + raw_item=request_item, + tool_name="list_repo_languages", + ) + context_wrapper = make_context_wrapper() + context_wrapper.approve_tool(approval_item) + processed_response = make_processed_response( + new_items=[MCPApprovalRequestItem(raw_item=request_item, agent=execution_agent)], + mcp_approval_requests=[ + ToolRunMCPApprovalRequest( + request_item=request_item, + mcp_tool=mcp_tool, + ) + ], + ) + + result = await turn_resolution.resolve_interrupted_turn( + bindings=_bind_agent(execution_agent), + original_input="test", + original_pre_step_items=[approval_item], + new_response=ModelResponse(output=[], usage=Usage(), response_id="resp"), + processed_response=processed_response, + hooks=RunHooks(), + context_wrapper=context_wrapper, + run_config=RunConfig(), + ) + + responses = [ + item + for item in result.new_step_items + if isinstance(item, MCPApprovalResponseItem) + and item.raw_item.get("approval_request_id") == "mcp-approval-resume-public-agent" + ] + assert responses + assert all(item.agent is public_agent for item in responses) + + +@pytest.mark.asyncio +async def test_execute_handoffs_uses_public_agent_for_ignored_extra_handoffs(): + """Ignored extra handoff outputs should stay owned by the public agent.""" + + first_target = Agent(name="alpha") + second_target = Agent(name="beta") + public_agent = Agent(name="triage", handoffs=[first_target, second_target]) + execution_agent = public_agent.clone() + set_public_agent(execution_agent, public_agent) + response = ModelResponse( + output=[get_handoff_tool_call(first_target), get_handoff_tool_call(second_target)], + usage=Usage(), + response_id="resp", + ) + + result = await get_execute_result(execution_agent, response) + + ignored_outputs = [ + item + for item in result.new_step_items + if isinstance(item, ToolCallOutputItem) + and item.output == "Multiple handoffs detected, ignoring this one." + ] + assert len(ignored_outputs) == 1 + assert ignored_outputs[0].agent is public_agent + + @pytest.mark.asyncio async def test_execute_tools_emits_hosted_mcp_rejection_response(): """Hosted MCP rejections without callbacks should emit approval responses.""" @@ -2836,7 +3041,7 @@ async def test_execute_tools_emits_hosted_mcp_rejection_response(): reject_tool_call(context_wrapper, agent, request_item, tool_name="list_repo_languages") result = await run_loop.execute_tools_and_side_effects( - agent=agent, + bindings=_bind_agent(agent), original_input="test", pre_step_items=[], new_response=ModelResponse(output=[], usage=Usage(), response_id="resp"), @@ -2897,7 +3102,7 @@ async def test_execute_tools_emits_hosted_mcp_rejection_reason_from_explicit_mes ) result = await run_loop.execute_tools_and_side_effects( - agent=agent, + bindings=_bind_agent(agent), original_input="test", pre_step_items=[], new_response=ModelResponse(output=[], usage=Usage(), response_id="resp"), diff --git a/tests/test_run_step_processing.py b/tests/test_run_step_processing.py index 2682ba647d..8d83193185 100644 --- a/tests/test_run_step_processing.py +++ b/tests/test_run_step_processing.py @@ -232,7 +232,7 @@ def fake_nest( monkeypatch.setattr("agents.run_internal.turn_resolution.nest_handoff_history", fake_nest) result = await run_loop.execute_handoffs( - agent=source_agent, + public_agent=source_agent, original_input=list(original_input), pre_step_items=pre_step_items, new_step_items=new_step_items, @@ -280,7 +280,7 @@ def fake_nest( monkeypatch.setattr("agents.run_internal.turn_resolution.nest_handoff_history", fake_nest) result = await run_loop.execute_handoffs( - agent=source_agent, + public_agent=source_agent, original_input=list(original_input), pre_step_items=pre_step_items, new_step_items=new_step_items, diff --git a/tests/test_sandbox_dependencies.py b/tests/test_sandbox_dependencies.py new file mode 100644 index 0000000000..ed282cf3e1 --- /dev/null +++ b/tests/test_sandbox_dependencies.py @@ -0,0 +1,169 @@ +from __future__ import annotations + +import pytest + +from agents.sandbox.session import ( + Dependencies, + DependenciesBindingError, + DependenciesMissingDependencyError, +) + + +class _AsyncClosable: + def __init__(self) -> None: + self.calls = 0 + + async def aclose(self) -> None: + self.calls += 1 + + +class _AsyncCloseMethod: + def __init__(self) -> None: + self.calls = 0 + + async def close(self) -> None: + self.calls += 1 + + +class _SyncClosable: + def __init__(self) -> None: + self.calls = 0 + + def close(self) -> None: + self.calls += 1 + + +@pytest.mark.asyncio +async def test_dependencies_with_values_binds_multiple_values() -> None: + key1 = "tests.with_values.str" + key2 = "tests.with_values.int" + dependencies = Dependencies.with_values({key1: "hello", key2: 123}) + + assert await dependencies.require(key1) == "hello" + assert await dependencies.require(key2) == 123 + + +@pytest.mark.asyncio +async def test_dependencies_bind_value_and_require() -> None: + dependencies = Dependencies() + key = "tests.value" + dependencies.bind_value(key, "hello") + + assert await dependencies.get(key) == "hello" + assert await dependencies.require(key, consumer="test") == "hello" + + +@pytest.mark.asyncio +async def test_dependencies_missing_dependency_includes_key_and_consumer() -> None: + dependencies = Dependencies() + key = "tests.missing" + + with pytest.raises(DependenciesMissingDependencyError, match="tests.missing"): + await dependencies.require(key, consumer="SedimentFile") + + +def test_dependencies_duplicate_binding_raises() -> None: + dependencies = Dependencies() + key = "tests.dup" + dependencies.bind_value(key, "a") + + with pytest.raises(DependenciesBindingError, match="already bound"): + dependencies.bind_value(key, "b") + + +def test_dependencies_empty_key_raises() -> None: + dependencies = Dependencies() + + with pytest.raises(ValueError, match="non-empty"): + dependencies.bind_value("", "x") + + with pytest.raises(ValueError, match="non-empty"): + dependencies.bind_factory("", lambda _dependencies: "x") + + +@pytest.mark.asyncio +async def test_dependencies_cached_factory_resolves_once() -> None: + dependencies = Dependencies() + key = "tests.cached_factory" + calls = 0 + + def _factory(_dependencies: Dependencies) -> str: + nonlocal calls + calls += 1 + return f"value-{calls}" + + dependencies.bind_factory(key, _factory, cache=True) + + assert await dependencies.require(key) == "value-1" + assert await dependencies.require(key) == "value-1" + assert calls == 1 + + +@pytest.mark.asyncio +async def test_dependencies_uncached_factory_resolves_every_time() -> None: + dependencies = Dependencies() + key = "tests.uncached_factory" + calls = 0 + + def _factory(_dependencies: Dependencies) -> str: + nonlocal calls + calls += 1 + return f"value-{calls}" + + dependencies.bind_factory(key, _factory, cache=False) + + assert await dependencies.require(key) == "value-1" + assert await dependencies.require(key) == "value-2" + assert calls == 2 + + +@pytest.mark.asyncio +async def test_dependencies_async_factory_supported() -> None: + dependencies = Dependencies() + key = "tests.async_factory" + + async def _factory(_dependencies: Dependencies) -> str: + return "async-value" + + dependencies.bind_factory(key, _factory) + assert await dependencies.require(key) == "async-value" + + +@pytest.mark.asyncio +async def test_dependencies_aclose_closes_owned_results_and_is_idempotent() -> None: + dependencies = Dependencies() + k1 = "tests.async_aclose" + k2 = "tests.async_close" + k3 = "tests.sync_close" + + dependencies.bind_factory(k1, lambda _deps: _AsyncClosable(), owns_result=True) + dependencies.bind_factory(k2, lambda _deps: _AsyncCloseMethod(), owns_result=True) + dependencies.bind_factory(k3, lambda _deps: _SyncClosable(), owns_result=True, cache=False) + + v1 = await dependencies.require(k1) + v2 = await dependencies.require(k2) + v3a = await dependencies.require(k3) + v3b = await dependencies.require(k3) + + assert v3a is not v3b + + await dependencies.aclose() + await dependencies.aclose() + + assert isinstance(v1, _AsyncClosable) and v1.calls == 1 + assert isinstance(v2, _AsyncCloseMethod) and v2.calls == 1 + assert isinstance(v3a, _SyncClosable) and v3a.calls == 1 + assert isinstance(v3b, _SyncClosable) and v3b.calls == 1 + + +@pytest.mark.asyncio +async def test_dependencies_bound_values_are_not_closed() -> None: + dependencies = Dependencies() + key = "tests.bound_value" + value = _SyncClosable() + dependencies.bind_value(key, value) + + _ = await dependencies.require(key) + await dependencies.aclose() + + assert value.calls == 0 diff --git a/tests/test_sandbox_docker.py b/tests/test_sandbox_docker.py new file mode 100644 index 0000000000..790b604605 --- /dev/null +++ b/tests/test_sandbox_docker.py @@ -0,0 +1,585 @@ +from __future__ import annotations + +import asyncio +import io +import shutil +import tarfile +from collections.abc import Callable +from pathlib import Path +from typing import cast + +import docker.errors # type: ignore[import-untyped] +import pytest + +import agents.sandbox.sandboxes.docker as docker_sandbox +from agents.sandbox.entries import ( + AzureBlobMount, + Dir, + File, + FuseMountPattern, + RcloneMountPattern, +) +from agents.sandbox.errors import ExecTimeoutError, InvalidManifestPathError +from agents.sandbox.manifest import Manifest +from agents.sandbox.sandboxes.docker import ( + DockerSandboxClient, + DockerSandboxSession, + DockerSandboxSessionState, + _manifest_requires_fuse, + _manifest_requires_sys_admin, +) +from agents.sandbox.snapshot import NoopSnapshot +from agents.sandbox.types import ExecResult + + +class _FakeDockerContainer: + def __init__(self, host_root: Path) -> None: + self._host_root = host_root + self.status = "running" + self.archive_calls: list[str] = [] + + def reload(self) -> None: + return + + def get_archive(self, path: str) -> tuple[object, dict[str, object]]: + self.archive_calls.append(path) + if path == "/workspace": + raise docker.errors.APIError("root archive unsupported") + + host_path = self._host_path(path) + buf = io.BytesIO() + with tarfile.open(fileobj=buf, mode="w") as tar: + tar.add(host_path, arcname=Path(path).name) + buf.seek(0) + return iter([buf.getvalue()]), {} + + def _host_path(self, path: str | Path) -> Path: + container_path = Path(path) + return self._host_root / container_path.relative_to("/") + + +class _PullRecorder: + def __init__(self) -> None: + self.calls: list[tuple[str, str | None, bool]] = [] + + def pull(self, repo: str, *, tag: str | None = None, all_tags: bool = False) -> None: + self.calls.append((repo, tag, all_tags)) + + +class _FakeDockerClient: + def __init__(self) -> None: + self.images = _PullRecorder() + + +class _HostBackedDockerSession(DockerSandboxSession): + def __init__(self, *, host_root: Path, manifest: Manifest) -> None: + container = _FakeDockerContainer(host_root) + state = DockerSandboxSessionState( + manifest=manifest, + snapshot=NoopSnapshot(id="snapshot"), + image="python:3.11-slim", + container_id="container", + ) + super().__init__( + docker_client=object(), + container=container, + state=state, + ) + self._host_root = host_root + self._fake_container = container + + async def _exec_internal( + self, + *command: str | Path, + timeout: float | None = None, + ) -> ExecResult: + _ = timeout + cmd = [str(part) for part in command] + if cmd[:2] == ["mkdir", "-p"]: + self._host_path(cmd[2]).mkdir(parents=True, exist_ok=True) + return ExecResult(stdout=b"", stderr=b"", exit_code=0) + if cmd[:3] == ["cp", "-R", "--"]: + src = self._host_path(cmd[3]) + dst = self._host_path(cmd[4]) + shutil.copytree(src, dst) + return ExecResult(stdout=b"", stderr=b"", exit_code=0) + if cmd[:2] == ["rm", "-rf"]: + target = self._host_path(cmd[3]) + if target.is_dir(): + shutil.rmtree(target, ignore_errors=True) + else: + try: + target.unlink() + except FileNotFoundError: + pass + return ExecResult(stdout=b"", stderr=b"", exit_code=0) + raise AssertionError(f"Unexpected command: {cmd!r}") + + def _host_path(self, path: str | Path) -> Path: + container_path = Path(path) + return self._host_root / container_path.relative_to("/") + + +def _archive_member_names(archive: io.IOBase) -> list[str]: + payload = archive.read() + if not isinstance(payload, bytes): + raise AssertionError(f"Expected bytes archive payload, got {type(payload)!r}") + with tarfile.open(fileobj=io.BytesIO(payload), mode="r:*") as tar: + return tar.getnames() + + +@pytest.mark.asyncio +async def test_docker_persist_workspace_stages_copy_before_get_archive( + tmp_path: Path, +) -> None: + host_root = tmp_path / "container" + workspace = host_root / "workspace" + workspace.mkdir(parents=True) + (workspace / "README.md").write_text("hello from workspace", encoding="utf-8") + + session = _HostBackedDockerSession( + host_root=host_root, + manifest=Manifest(root="/workspace"), + ) + + archive = await session.persist_workspace() + + names = _archive_member_names(archive) + + assert "/workspace" not in session._fake_container.archive_calls + assert any(name.endswith("workspace") for name in names) + assert any(name.endswith("workspace/README.md") for name in names) + + +@pytest.mark.asyncio +async def test_docker_persist_workspace_prunes_ephemeral_entries_from_staged_copy( + tmp_path: Path, +) -> None: + host_root = tmp_path / "container" + workspace = host_root / "workspace" + workspace.mkdir(parents=True) + (workspace / "keep.txt").write_text("keep", encoding="utf-8") + (workspace / "skip.txt").write_text("skip", encoding="utf-8") + + session = _HostBackedDockerSession( + host_root=host_root, + manifest=Manifest( + root="/workspace", + entries={ + "skip.txt": File(content=b"skip", ephemeral=True), + }, + ), + ) + + archive = await session.persist_workspace() + + names = _archive_member_names(archive) + + assert any(name.endswith("workspace/keep.txt") for name in names) + assert not any(name.endswith("workspace/skip.txt") for name in names) + + +@pytest.mark.asyncio +async def test_docker_read_and_write_reject_paths_outside_workspace_root(tmp_path: Path) -> None: + host_root = tmp_path / "container" + workspace = host_root / "workspace" + workspace.mkdir(parents=True) + + session = _HostBackedDockerSession( + host_root=host_root, + manifest=Manifest(root="/workspace"), + ) + + with pytest.raises(InvalidManifestPathError, match="must not escape root"): + await session.read(Path("../secret.txt")) + with pytest.raises(InvalidManifestPathError, match="must not escape root"): + await session.write(Path("../secret.txt"), io.BytesIO(b"nope")) + + +def test_manifest_requires_fuse_detects_nested_mounts() -> None: + manifest = Manifest( + entries={ + "workspace": Dir( + children={ + "mount": AzureBlobMount( + account="account", + container="container", + mount_pattern=FuseMountPattern(), + ) + } + ) + } + ) + + assert _manifest_requires_fuse(manifest) is True + + +def test_manifest_requires_sys_admin_detects_nested_mounts() -> None: + manifest = Manifest( + entries={ + "workspace": Dir( + children={ + "mount": AzureBlobMount( + account="account", + container="container", + mount_pattern=RcloneMountPattern(mode="nfs"), + ) + } + ) + } + ) + + assert _manifest_requires_sys_admin(manifest) is True + + +@pytest.mark.asyncio +async def test_docker_create_container_parses_registry_port_image_refs( + monkeypatch: pytest.MonkeyPatch, +) -> None: + docker_client = _FakeDockerClient() + client = DockerSandboxClient(docker_client=cast(object, docker_client)) + + def _missing_image(_image: str) -> bool: + return False + + monkeypatch.setattr(client, "image_exists", _missing_image) + with pytest.raises(AssertionError): + await client._create_container("localhost:5000/myimg:latest") + + assert docker_client.images.calls == [("localhost:5000/myimg", "latest", False)] + + +class _ExecRunContainer: + def __init__(self, *, workspace_exists: bool = False) -> None: + self.exec_calls: list[dict[str, object]] = [] + self._workspace_exists = workspace_exists + + def exec_run( + self, + cmd: list[str], + demux: bool = True, + workdir: str | None = None, + ) -> object: + self.exec_calls.append({"cmd": cmd, "demux": demux, "workdir": workdir}) + exit_code = 0 + if cmd == ["test", "-d", "--", "/workspace"]: + exit_code = 0 if self._workspace_exists else 1 + return type( + "_ExecResult", + (), + {"output": (b"", b""), "exit_code": exit_code}, + )() + + +class _ResumeDockerClient: + def __init__(self, container: object) -> None: + self._container = container + self.containers = self + + def get(self, container_id: str) -> object: + _ = container_id + if isinstance(self._container, BaseException): + raise self._container + return self._container + + +class _PositionalOnlyMissingDockerClient: + def __init__(self) -> None: + self.containers = self + + def get(self, container_id: str, /) -> object: + _ = container_id + raise docker.errors.NotFound("missing") + + +class _ResumeContainer: + def __init__( + self, + *, + status: str, + container_id: str = "container", + workspace_exists: bool = False, + ) -> None: + self.status = status + self.id = container_id + self.exec_calls: list[dict[str, object]] = [] + self._workspace_exists = workspace_exists + + def reload(self) -> None: + return + + def exec_run( + self, + cmd: list[str], + demux: bool = True, + workdir: str | None = None, + ) -> object: + self.exec_calls.append({"cmd": cmd, "demux": demux, "workdir": workdir}) + exit_code = 0 + if cmd == ["test", "-d", "--", "/workspace"]: + exit_code = 0 if self._workspace_exists else 1 + return type( + "_ExecResult", + (), + {"output": (b"", b""), "exit_code": exit_code}, + )() + + +@pytest.mark.asyncio +async def test_docker_exec_timeout_uses_shared_executor(monkeypatch: pytest.MonkeyPatch) -> None: + container = _ExecRunContainer() + session = DockerSandboxSession( + docker_client=object(), + container=container, + state=DockerSandboxSessionState( + manifest=Manifest(root="/workspace"), + snapshot=NoopSnapshot(id="snapshot"), + image="python:3.11-slim", + container_id="container", + ), + ) + + submitted_executors: list[object] = [] + loop = asyncio.get_running_loop() + + def fake_run_in_executor(executor: object, func: object) -> asyncio.Future[object]: + _ = func + submitted_executors.append(executor) + return asyncio.Future() + + monkeypatch.setattr(loop, "run_in_executor", fake_run_in_executor) + + with pytest.raises(ExecTimeoutError): + await session._exec_internal("sleep", "10", timeout=0.01) + with pytest.raises(ExecTimeoutError): + await session._exec_internal("sleep", "20", timeout=0.01) + + assert submitted_executors == [ + docker_sandbox._DOCKER_EXECUTOR, + docker_sandbox._DOCKER_EXECUTOR, + ] + assert container.exec_calls == [ + { + "cmd": ["sh", "-lc", "pkill -f -- 'sleep 10' >/dev/null 2>&1 || true"], + "demux": True, + "workdir": None, + }, + { + "cmd": ["sh", "-lc", "pkill -f -- 'sleep 20' >/dev/null 2>&1 || true"], + "demux": True, + "workdir": None, + }, + ] + + +@pytest.mark.asyncio +async def test_docker_exec_omits_workdir_until_workspace_ready( + monkeypatch: pytest.MonkeyPatch, +) -> None: + container = _ExecRunContainer() + session = DockerSandboxSession( + docker_client=object(), + container=container, + state=DockerSandboxSessionState( + manifest=Manifest(root="/workspace"), + snapshot=NoopSnapshot(id="snapshot"), + image="python:3.11-slim", + container_id="container", + ), + ) + + loop = asyncio.get_running_loop() + + def fake_run_in_executor( + executor: object, func: Callable[[], object] + ) -> asyncio.Future[object]: + _ = executor + future: asyncio.Future[object] = asyncio.Future() + future.set_result(func()) + return future + + monkeypatch.setattr(loop, "run_in_executor", fake_run_in_executor) + + result = await session._exec_internal("find", ".", timeout=0.01) + + assert result.ok() + assert container.exec_calls == [ + { + "cmd": ["find", "."], + "demux": True, + "workdir": None, + } + ] + + +@pytest.mark.asyncio +async def test_docker_exec_uses_manifest_root_as_workdir_after_workspace_ready( + monkeypatch: pytest.MonkeyPatch, +) -> None: + container = _ExecRunContainer() + session = DockerSandboxSession( + docker_client=object(), + container=container, + state=DockerSandboxSessionState( + manifest=Manifest(root="/workspace"), + snapshot=NoopSnapshot(id="snapshot"), + image="python:3.11-slim", + container_id="container", + ), + ) + session._workspace_root_ready = True + + loop = asyncio.get_running_loop() + + def fake_run_in_executor( + executor: object, func: Callable[[], object] + ) -> asyncio.Future[object]: + _ = executor + future: asyncio.Future[object] = asyncio.Future() + future.set_result(func()) + return future + + monkeypatch.setattr(loop, "run_in_executor", fake_run_in_executor) + + result = await session._exec_internal("find", ".", timeout=0.01) + + assert result.ok() + assert container.exec_calls == [ + { + "cmd": ["find", "."], + "demux": True, + "workdir": "/workspace", + } + ] + + +@pytest.mark.asyncio +async def test_docker_resume_preserves_workspace_readiness_from_state() -> None: + client = DockerSandboxClient( + docker_client=_ResumeDockerClient(_ResumeContainer(status="running")) + ) + + ready_session = await client.resume( + DockerSandboxSessionState( + manifest=Manifest(root="/workspace"), + snapshot=NoopSnapshot(id="snapshot"), + image="python:3.11-slim", + container_id="container", + workspace_root_ready=True, + ) + ) + not_ready_session = await client.resume( + DockerSandboxSessionState( + manifest=Manifest(root="/workspace"), + snapshot=NoopSnapshot(id="snapshot"), + image="python:3.11-slim", + container_id="container", + workspace_root_ready=False, + ) + ) + + assert isinstance(ready_session._inner, DockerSandboxSession) + assert ready_session._inner._workspace_root_ready is True + assert ready_session._inner.should_provision_manifest_accounts_on_resume() is False + assert isinstance(not_ready_session._inner, DockerSandboxSession) + assert not_ready_session._inner._workspace_root_ready is False + assert not_ready_session._inner.should_provision_manifest_accounts_on_resume() is False + + +@pytest.mark.asyncio +async def test_docker_resume_resets_workspace_readiness_when_container_is_recreated( + monkeypatch: pytest.MonkeyPatch, +) -> None: + client = DockerSandboxClient( + docker_client=cast(object, _ResumeDockerClient(docker.errors.NotFound("missing"))) + ) + replacement = _ResumeContainer(status="created", container_id="replacement") + + async def _fake_create_container(image: str, *, manifest: Manifest | None = None) -> object: + _ = (image, manifest) + return replacement + + monkeypatch.setattr(client, "_create_container", _fake_create_container) + + resumed = await client.resume( + DockerSandboxSessionState( + manifest=Manifest(root="/workspace"), + snapshot=NoopSnapshot(id="snapshot"), + image="python:3.11-slim", + container_id="missing", + workspace_root_ready=True, + ) + ) + + assert isinstance(resumed._inner, DockerSandboxSession) + inner = resumed._inner + assert inner.state.container_id == "replacement" + assert inner.state.workspace_root_ready is False + assert inner._workspace_root_ready is False + assert inner.should_provision_manifest_accounts_on_resume() is True + + +@pytest.mark.asyncio +async def test_docker_resume_recovers_workspace_workdir_when_root_already_exists( + monkeypatch: pytest.MonkeyPatch, +) -> None: + container = _ResumeContainer(status="running", workspace_exists=True) + client = DockerSandboxClient(docker_client=_ResumeDockerClient(container)) + + payload = DockerSandboxSessionState( + manifest=Manifest(root="/workspace"), + snapshot=NoopSnapshot(id="snapshot"), + image="python:3.11-slim", + container_id="container", + workspace_root_ready=True, + ).model_dump(mode="json") + payload.pop("workspace_root_ready") + + resumed = await client.resume(client.deserialize_session_state(payload)) + assert isinstance(resumed._inner, DockerSandboxSession) + + loop = asyncio.get_running_loop() + + def fake_run_in_executor( + executor: object, func: Callable[[], object] + ) -> asyncio.Future[object]: + _ = executor + future: asyncio.Future[object] = asyncio.Future() + future.set_result(func()) + return future + + monkeypatch.setattr(loop, "run_in_executor", fake_run_in_executor) + + result = await resumed._inner._exec_internal("find", ".", timeout=0.01) + + assert result.ok() + assert resumed._inner.state.workspace_root_ready is True + assert resumed._inner._workspace_root_ready is True + assert container.exec_calls == [ + { + "cmd": ["test", "-d", "--", "/workspace"], + "demux": True, + "workdir": None, + }, + { + "cmd": ["find", "."], + "demux": True, + "workdir": "/workspace", + }, + ] + + +@pytest.mark.asyncio +async def test_docker_exists_returns_false_for_missing_container() -> None: + session = DockerSandboxSession( + docker_client=cast(object, _PositionalOnlyMissingDockerClient()), + container=_ResumeContainer(status="running"), + state=DockerSandboxSessionState( + manifest=Manifest(root="/workspace"), + snapshot=NoopSnapshot(id="snapshot"), + image="python:3.11-slim", + container_id="missing", + ), + ) + + assert await session.exists() is False diff --git a/tests/test_sandbox_entries.py b/tests/test_sandbox_entries.py new file mode 100644 index 0000000000..bf4e81cdf4 --- /dev/null +++ b/tests/test_sandbox_entries.py @@ -0,0 +1,126 @@ +from __future__ import annotations + +import io +from pathlib import Path + +import pytest + +from agents.sandbox.entries import Dir, GitRepo, LocalFile +from agents.sandbox.manifest import Manifest +from agents.sandbox.session.base_sandbox_session import BaseSandboxSession +from agents.sandbox.session.sandbox_session_state import SandboxSessionState +from agents.sandbox.snapshot import NoopSnapshot +from agents.sandbox.types import ExecResult + + +class _RecordingSession(BaseSandboxSession): + def __init__(self, manifest: Manifest | None = None) -> None: + self.state = SandboxSessionState( + manifest=manifest or Manifest(), + snapshot=NoopSnapshot(id="noop"), + ) + self.exec_calls: list[tuple[str, ...]] = [] + self.writes: dict[Path, bytes] = {} + + async def _exec_internal( + self, + *command: str | Path, + timeout: float | None = None, + ) -> ExecResult: + _ = timeout + cmd = tuple(str(part) for part in command) + self.exec_calls.append(cmd) + return ExecResult(stdout=b"", stderr=b"", exit_code=0) + + async def read(self, path: Path) -> io.IOBase: + return io.BytesIO(self.writes[path]) + + async def write(self, path: Path, data: io.IOBase) -> None: + self.writes[path] = data.read() + + async def running(self) -> bool: + return True + + async def persist_workspace(self) -> io.IOBase: + return io.BytesIO() + + async def hydrate_workspace(self, data: io.IOBase) -> None: + _ = data + + async def shutdown(self) -> None: + return + + +class _GitRefSession(_RecordingSession): + async def _exec_internal( + self, + *command: str | Path, + timeout: float | None = None, + ) -> ExecResult: + _ = timeout + cmd = tuple(str(part) for part in command) + self.exec_calls.append(cmd) + if cmd == ("command -v git >/dev/null 2>&1",): + return ExecResult(stdout=b"/usr/bin/git\n", stderr=b"", exit_code=0) + if cmd[:2] == ("git", "clone"): + return ExecResult(stdout=b"", stderr=b"unexpected clone path", exit_code=1) + return ExecResult(stdout=b"", stderr=b"", exit_code=0) + + +@pytest.mark.asyncio +async def test_base_sandbox_session_uses_current_working_directory_for_local_file_sources( + monkeypatch: pytest.MonkeyPatch, + tmp_path: Path, +) -> None: + source = tmp_path / "source.txt" + source.write_text("hello", encoding="utf-8") + monkeypatch.chdir(tmp_path) + session = _RecordingSession( + Manifest(entries={"copied.txt": LocalFile(src=Path("source.txt"))}), + ) + + result = await session.apply_manifest() + + assert result.files[0].path == Path("/workspace/copied.txt") + assert session.writes[Path("/workspace/copied.txt")] == b"hello" + + +@pytest.mark.asyncio +async def test_git_repo_uses_fetch_checkout_path_for_commit_refs() -> None: + session = _GitRefSession() + repo = GitRepo(repo="openai/example", ref="deadbeef") + + await repo.apply(session, Path("/workspace/repo"), Path("/ignored")) + + assert not any(call[:2] == ("git", "clone") for call in session.exec_calls) + assert any(call[:2] == ("git", "init") for call in session.exec_calls) + assert any( + len(call) >= 7 + and call[:2] == ("git", "-C") + and call[3:6] == ("remote", "add", "origin") + and call[6] == "https://github.com/openai/example.git" + for call in session.exec_calls + ) + assert any( + len(call) >= 9 + and call[:2] == ("git", "-C") + and call[3:7] == ("fetch", "--depth", "1", "--no-tags") + and call[-2:] == ("origin", "deadbeef") + for call in session.exec_calls + ) + assert any( + len(call) >= 6 + and call[:2] == ("git", "-C") + and call[3:5] == ("checkout", "--detach") + and call[-1] == "FETCH_HEAD" + for call in session.exec_calls + ) + + +@pytest.mark.asyncio +async def test_dir_metadata_strips_file_type_bits_before_chmod() -> None: + session = _RecordingSession() + + await Dir()._apply_metadata(session, Path("/workspace/dir")) + + assert ("chmod", "0755", "/workspace/dir") in session.exec_calls diff --git a/tests/test_sandbox_extract.py b/tests/test_sandbox_extract.py new file mode 100644 index 0000000000..989584ccb2 --- /dev/null +++ b/tests/test_sandbox_extract.py @@ -0,0 +1,279 @@ +from __future__ import annotations + +import io +import os +import tarfile +import zipfile +from pathlib import Path + +import pytest + +from agents.sandbox.errors import InvalidManifestPathError, WorkspaceArchiveWriteError +from agents.sandbox.files import EntryKind, FileEntry +from agents.sandbox.manifest import Manifest +from agents.sandbox.sandboxes.unix_local import ( + UnixLocalSandboxSession, + UnixLocalSandboxSessionState, +) +from agents.sandbox.session.base_sandbox_session import BaseSandboxSession +from agents.sandbox.snapshot import NoopSnapshot +from agents.sandbox.types import ExecResult, Permissions + + +def _build_session(tmp_path: Path) -> UnixLocalSandboxSession: + state = UnixLocalSandboxSessionState( + manifest=Manifest(root=str(tmp_path / "workspace")), + snapshot=NoopSnapshot(id="noop"), + ) + return UnixLocalSandboxSession.from_state(state) + + +class _CountingExtractSession(BaseSandboxSession): + def __init__(self, workspace_root: Path) -> None: + self.state = UnixLocalSandboxSessionState( + manifest=Manifest(root=str(workspace_root)), + snapshot=NoopSnapshot(id="noop"), + ) + self.ls_calls: list[Path] = [] + + async def _exec_internal( + self, + *command: str | Path, + timeout: float | None = None, + ) -> ExecResult: + _ = (command, timeout) + raise AssertionError("exec() should not be called in this test") + + async def read(self, path: Path) -> io.IOBase: + return self.normalize_path(path).open("rb") + + async def write(self, path: Path, data: io.IOBase) -> None: + workspace_path = self.normalize_path(path) + workspace_path.parent.mkdir(parents=True, exist_ok=True) + payload = data.read() + if isinstance(payload, str): + payload = payload.encode("utf-8") + workspace_path.write_bytes(payload) + + async def running(self) -> bool: + return True + + async def persist_workspace(self) -> io.IOBase: + return io.BytesIO() + + async def hydrate_workspace(self, data: io.IOBase) -> None: + _ = data + + async def shutdown(self) -> None: + return + + async def mkdir(self, path: Path | str, *, parents: bool = False) -> None: + self.normalize_path(path).mkdir(parents=parents, exist_ok=True) + + async def ls(self, path: Path | str) -> list[FileEntry]: + directory = self.normalize_path(path) + self.ls_calls.append(directory) + if not directory.exists(): + raise AssertionError(f"ls() called for missing directory: {directory}") + + entries: list[FileEntry] = [] + for child in directory.iterdir(): + if child.is_symlink(): + kind = EntryKind.SYMLINK + elif child.is_dir(): + kind = EntryKind.DIRECTORY + else: + kind = EntryKind.FILE + entries.append( + FileEntry( + path=str(child), + permissions=Permissions(), + owner="root", + group="root", + size=0, + kind=kind, + ) + ) + return entries + + +def _tar_bytes(*, members: dict[str, bytes]) -> io.BytesIO: + buf = io.BytesIO() + with tarfile.open(fileobj=buf, mode="w") as archive: + for name, payload in members.items(): + info = tarfile.TarInfo(name=name) + info.size = len(payload) + archive.addfile(info, io.BytesIO(payload)) + buf.seek(0) + return buf + + +def _zip_bytes(*, members: dict[str, bytes]) -> io.BytesIO: + buf = io.BytesIO() + with zipfile.ZipFile(buf, mode="w") as archive: + for name, payload in members.items(): + archive.writestr(name, payload) + buf.seek(0) + return buf + + +@pytest.mark.asyncio +async def test_extract_tar_writes_archive_and_unpacks_contents(tmp_path: Path) -> None: + session = _build_session(tmp_path) + await session.start() + try: + await session.extract( + "bundle.tar", + _tar_bytes(members={"nested/hello.txt": b"hello from tar"}), + ) + finally: + await session.shutdown() + + workspace = Path(session.state.manifest.root) + assert (workspace / "bundle.tar").is_file() + assert (workspace / "nested" / "hello.txt").read_text(encoding="utf-8") == "hello from tar" + + +@pytest.mark.asyncio +async def test_extract_zip_writes_archive_and_unpacks_contents(tmp_path: Path) -> None: + session = _build_session(tmp_path) + await session.start() + try: + await session.extract( + "bundle.zip", + _zip_bytes(members={"nested/hello.txt": b"hello from zip"}), + ) + finally: + await session.shutdown() + + workspace = Path(session.state.manifest.root) + assert (workspace / "bundle.zip").is_file() + assert (workspace / "nested" / "hello.txt").read_text(encoding="utf-8") == "hello from zip" + + +class _NoSeekableZipStream(io.IOBase): + def __init__(self, payload: bytes) -> None: + self._buffer = io.BytesIO(payload) + + def tell(self) -> int: + return self._buffer.tell() + + def seek(self, offset: int, whence: int = io.SEEK_SET) -> int: + return self._buffer.seek(offset, whence) + + def read(self, size: int = -1) -> bytes: + return self._buffer.read(size) + + +def test_zipfile_compatible_stream_wraps_streams_without_seekable() -> None: + raw_stream = _NoSeekableZipStream(_zip_bytes(members={"file.txt": b"hello"}).getvalue()) + + compatible = BaseSandboxSession._zipfile_compatible_stream(raw_stream) + + with zipfile.ZipFile(compatible) as archive: + assert archive.read("file.txt") == b"hello" + + +@pytest.mark.asyncio +async def test_extract_tar_rejects_symlinked_parent_paths(tmp_path: Path) -> None: + session = _build_session(tmp_path) + await session.start() + try: + workspace = Path(session.state.manifest.root) + outside = tmp_path / "outside" + outside.mkdir() + os.symlink(outside, workspace / "link", target_is_directory=True) + + with pytest.raises(WorkspaceArchiveWriteError) as exc_info: + await session.extract( + "bundle.tar", + _tar_bytes(members={"link/hello.txt": b"hello from tar"}), + ) + + assert exc_info.value.context["member"] == "link/hello.txt" + assert exc_info.value.context["reason"] == "symlink in parent path: link" + assert not (outside / "hello.txt").exists() + finally: + await session.shutdown() + + +@pytest.mark.asyncio +async def test_extract_zip_rejects_symlinked_parent_paths(tmp_path: Path) -> None: + session = _build_session(tmp_path) + await session.start() + try: + workspace = Path(session.state.manifest.root) + outside = tmp_path / "outside" + outside.mkdir() + os.symlink(outside, workspace / "link", target_is_directory=True) + + with pytest.raises(WorkspaceArchiveWriteError) as exc_info: + await session.extract( + "bundle.zip", + _zip_bytes(members={"link/hello.txt": b"hello from zip"}), + ) + + assert exc_info.value.context["member"] == "link/hello.txt" + assert exc_info.value.context["reason"] == "symlink in parent path: link" + assert not (outside / "hello.txt").exists() + finally: + await session.shutdown() + + +@pytest.mark.asyncio +async def test_extract_tar_reuses_directory_listings_during_symlink_checks(tmp_path: Path) -> None: + workspace = tmp_path / "workspace" + workspace.mkdir() + session = _CountingExtractSession(workspace) + + await session.extract( + "bundle.tar", + _tar_bytes( + members={ + "nested/one.txt": b"one", + "nested/two.txt": b"two", + } + ), + ) + + assert (workspace / "nested" / "one.txt").read_text(encoding="utf-8") == "one" + assert (workspace / "nested" / "two.txt").read_text(encoding="utf-8") == "two" + assert session.ls_calls == [ + workspace, + workspace / "nested", + ] + + +@pytest.mark.asyncio +async def test_unix_local_helpers_reject_paths_outside_workspace_root(tmp_path: Path) -> None: + session = _build_session(tmp_path) + await session.start() + try: + with pytest.raises(InvalidManifestPathError, match="must not escape root"): + await session.ls("../outside") + with pytest.raises(InvalidManifestPathError, match="must not escape root"): + await session.mkdir("../outside", parents=True) + with pytest.raises(InvalidManifestPathError, match="must not escape root"): + await session.rm("../outside") + with pytest.raises(InvalidManifestPathError, match="must be relative"): + await session.extract("/tmp/bundle.tar", _tar_bytes(members={"a.txt": b"a"})) + finally: + await session.shutdown() + + +@pytest.mark.asyncio +async def test_unix_local_helpers_reject_symlink_escape_paths(tmp_path: Path) -> None: + session = _build_session(tmp_path) + await session.start() + try: + workspace = Path(session.state.manifest.root) + outside = tmp_path / "outside" + outside.mkdir() + os.symlink(outside, workspace / "link", target_is_directory=True) + + with pytest.raises(InvalidManifestPathError, match="must not escape root"): + await session.mkdir("link/nested", parents=True) + with pytest.raises(InvalidManifestPathError, match="must not escape root"): + await session.ls("link") + finally: + await session.shutdown() diff --git a/tests/test_sandbox_manifest.py b/tests/test_sandbox_manifest.py new file mode 100644 index 0000000000..f7a583b15f --- /dev/null +++ b/tests/test_sandbox_manifest.py @@ -0,0 +1,74 @@ +from pathlib import Path + +import pytest + +from agents.sandbox.entries import Dir, File, GCSMount +from agents.sandbox.errors import InvalidManifestPathError +from agents.sandbox.manifest import Manifest + + +def test_manifest_rejects_nested_child_paths_that_escape_workspace() -> None: + manifest = Manifest( + entries={ + "safe": Dir( + children={ + "../outside.txt": File(content=b"nope"), + } + ) + } + ) + + with pytest.raises(InvalidManifestPathError, match="must not escape root"): + manifest.validated_entries() + + +def test_manifest_rejects_nested_absolute_child_paths() -> None: + manifest = Manifest( + entries={ + "safe": Dir( + children={ + "/tmp/outside.txt": File(content=b"nope"), + } + ) + } + ) + + with pytest.raises(InvalidManifestPathError, match="must be relative"): + manifest.validated_entries() + + +def test_manifest_ephemeral_entry_paths_include_nested_children() -> None: + manifest = Manifest( + entries={ + "dir": Dir( + children={ + "keep.txt": File(content=b"keep"), + "tmp.txt": File(content=b"tmp", ephemeral=True), + } + ) + } + ) + + assert manifest.ephemeral_entry_paths() == {Path("dir/tmp.txt")} + + +def test_manifest_describe_preserves_tree_rendering_after_renderer_extract() -> None: + manifest = Manifest( + root="/workspace", + entries={ + "repo": Dir( + description="project root", + children={ + "README.md": File(content=b"hi", description="overview"), + }, + ), + "data": GCSMount(bucket="bucket", description="shared data"), + }, + ) + + assert manifest.describe(depth=2) == ( + "/workspace\n" + "├── data/ # /workspace/data — shared data\n" + "└── repo/ # /workspace/repo — project root\n" + " └── README.md # /workspace/repo/README.md — overview\n" + ) diff --git a/tests/test_sandbox_manifest_application.py b/tests/test_sandbox_manifest_application.py new file mode 100644 index 0000000000..22680d091d --- /dev/null +++ b/tests/test_sandbox_manifest_application.py @@ -0,0 +1,293 @@ +from __future__ import annotations + +import asyncio +from pathlib import Path + +import pytest + +from agents.sandbox.entries import Dir, File, GCSMount +from agents.sandbox.manifest import Manifest +from agents.sandbox.materialization import MaterializedFile +from agents.sandbox.session.manifest_application import ManifestApplier +from agents.sandbox.types import ExecResult, Group, User + + +def _materialized(dest: Path) -> list[MaterializedFile]: + return [MaterializedFile(path=dest, sha256=dest.as_posix())] + + +@pytest.mark.asyncio +async def test_manifest_applier_only_applies_ephemeral_entries_without_account_provisioning() -> ( + None +): + mkdir_calls: list[Path] = [] + exec_calls: list[tuple[str, ...]] = [] + apply_calls: list[tuple[str, Path, Path]] = [] + + async def mkdir(path: Path) -> None: + mkdir_calls.append(path) + + async def exec_checked_nonzero(*command: str) -> ExecResult: + exec_calls.append(command) + return ExecResult(stdout=b"", stderr=b"", exit_code=0) + + async def apply_entry(entry: object, dest: Path, base_dir: Path) -> list[MaterializedFile]: + apply_calls.append((type(entry).__name__, dest, base_dir)) + return _materialized(dest) + + applier = ManifestApplier( + mkdir=mkdir, + exec_checked_nonzero=exec_checked_nonzero, + apply_entry=apply_entry, + ) + manifest = Manifest( + root="/workspace", + entries={ + "keep.txt": File(content=b"keep"), + "tmp.txt": File(content=b"tmp", ephemeral=True), + }, + users=[User(name="alice")], + groups=[Group(name="dev", users=[User(name="alice")])], + ) + + result = await applier.apply_manifest(manifest, only_ephemeral=True) + + assert mkdir_calls == [Path("/workspace")] + assert exec_calls == [] + assert apply_calls == [("File", Path("/workspace/tmp.txt"), Path("/"))] + assert result.files == _materialized(Path("/workspace/tmp.txt")) + + +@pytest.mark.asyncio +async def test_manifest_applier_only_ephemeral_reapplies_nested_ephemeral_children() -> None: + apply_calls: list[tuple[str, Path, Path]] = [] + + async def mkdir(_path: Path) -> None: + return None + + async def exec_checked_nonzero(*_command: str) -> ExecResult: + return ExecResult(stdout=b"", stderr=b"", exit_code=0) + + async def apply_entry(entry: object, dest: Path, base_dir: Path) -> list[MaterializedFile]: + apply_calls.append((type(entry).__name__, dest, base_dir)) + return _materialized(dest) + + applier = ManifestApplier( + mkdir=mkdir, + exec_checked_nonzero=exec_checked_nonzero, + apply_entry=apply_entry, + ) + manifest = Manifest( + root="/workspace", + entries={ + "dir": Dir( + children={ + "keep.txt": File(content=b"keep"), + "tmp.txt": File(content=b"tmp", ephemeral=True), + } + ) + }, + ) + + result = await applier.apply_manifest(manifest, only_ephemeral=True) + + assert apply_calls == [("File", Path("/workspace/dir/tmp.txt"), Path("/"))] + assert result.files == _materialized(Path("/workspace/dir/tmp.txt")) + + +@pytest.mark.asyncio +async def test_manifest_applier_only_ephemeral_reapplies_full_ephemeral_directories() -> None: + applied_entries: list[tuple[object, Path, Path]] = [] + + async def mkdir(_path: Path) -> None: + return None + + async def exec_checked_nonzero(*_command: str) -> ExecResult: + return ExecResult(stdout=b"", stderr=b"", exit_code=0) + + async def apply_entry(entry: object, dest: Path, base_dir: Path) -> list[MaterializedFile]: + applied_entries.append((entry, dest, base_dir)) + return _materialized(dest) + + applier = ManifestApplier( + mkdir=mkdir, + exec_checked_nonzero=exec_checked_nonzero, + apply_entry=apply_entry, + ) + manifest = Manifest( + root="/workspace", + entries={ + "tmp": Dir( + ephemeral=True, + children={ + "keep.txt": File(content=b"keep"), + "nested": Dir(children={"child.txt": File(content=b"child")}), + "tmp.txt": File(content=b"tmp", ephemeral=True), + }, + ) + }, + ) + + result = await applier.apply_manifest(manifest, only_ephemeral=True) + + assert len(applied_entries) == 1 + entry, dest, base_dir = applied_entries[0] + assert isinstance(entry, Dir) + assert dest == Path("/workspace/tmp") + assert base_dir == Path("/") + assert set(entry.children) == {"keep.txt", "nested", "tmp.txt"} + assert result.files == _materialized(Path("/workspace/tmp")) + + +@pytest.mark.asyncio +async def test_manifest_applier_respects_explicit_base_dir() -> None: + apply_calls: list[tuple[str, Path, Path]] = [] + + async def mkdir(_path: Path) -> None: + return None + + async def exec_checked_nonzero(*_command: str) -> ExecResult: + return ExecResult(stdout=b"", stderr=b"", exit_code=0) + + async def apply_entry(entry: object, dest: Path, base_dir: Path) -> list[MaterializedFile]: + apply_calls.append((type(entry).__name__, dest, base_dir)) + return _materialized(dest) + + applier = ManifestApplier( + mkdir=mkdir, + exec_checked_nonzero=exec_checked_nonzero, + apply_entry=apply_entry, + ) + manifest = Manifest(entries={"file.txt": File(content=b"hello")}) + + result = await applier.apply_manifest(manifest, base_dir=Path("/tmp/project")) + + assert apply_calls == [("File", Path("/workspace/file.txt"), Path("/tmp/project"))] + assert result.files == _materialized(Path("/workspace/file.txt")) + + +@pytest.mark.asyncio +async def test_manifest_applier_provisions_groups_and_unique_users_before_entries() -> None: + exec_calls: list[tuple[str, ...]] = [] + + async def mkdir(_path: Path) -> None: + return None + + async def exec_checked_nonzero(*command: str) -> ExecResult: + exec_calls.append(command) + return ExecResult(stdout=b"", stderr=b"", exit_code=0) + + async def apply_entry(_entry: object, _dest: Path, _base_dir: Path) -> list[MaterializedFile]: + return [] + + applier = ManifestApplier( + mkdir=mkdir, + exec_checked_nonzero=exec_checked_nonzero, + apply_entry=apply_entry, + ) + manifest = Manifest( + users=[User(name="alice")], + groups=[Group(name="dev", users=[User(name="alice"), User(name="bob")])], + ) + + result = await applier.apply_manifest(manifest) + + assert result.files == [] + assert exec_calls[0] == ("groupadd", "dev") + assert exec_calls.count(("groupadd", "alice")) == 0 + assert exec_calls.count(("groupadd", "bob")) == 0 + assert ("useradd", "-U", "-M", "-s", "/usr/sbin/nologin", "alice") in exec_calls + assert ("useradd", "-U", "-M", "-s", "/usr/sbin/nologin", "bob") in exec_calls + assert ("usermod", "-aG", "dev", "alice") in exec_calls + assert ("usermod", "-aG", "dev", "bob") in exec_calls + + +@pytest.mark.asyncio +async def test_apply_entry_batch_flushes_parallel_work_before_overlapping_paths() -> None: + events: list[tuple[str, Path]] = [] + + async def mkdir(_path: Path) -> None: + return None + + async def exec_checked_nonzero(*_command: str) -> ExecResult: + return ExecResult(stdout=b"", stderr=b"", exit_code=0) + + async def apply_entry(_entry: object, dest: Path, _base_dir: Path) -> list[MaterializedFile]: + events.append(("start", dest)) + await asyncio.sleep(0) + events.append(("end", dest)) + return _materialized(dest) + + applier = ManifestApplier( + mkdir=mkdir, + exec_checked_nonzero=exec_checked_nonzero, + apply_entry=apply_entry, + ) + destinations = [ + Path("/workspace/alpha.txt"), + Path("/workspace/beta.txt"), + Path("/workspace/nested"), + Path("/workspace/nested/child.txt"), + ] + + files = await applier._apply_entry_batch( + [ + (destinations[0], File(content=b"a")), + (destinations[1], File(content=b"b")), + (destinations[2], Dir()), + (destinations[3], File(content=b"c")), + ], + base_dir=Path("/"), + ) + + assert [file.path for file in files] == destinations + child_start = events.index(("start", destinations[3])) + assert events.index(("end", destinations[0])) < child_start + assert events.index(("end", destinations[1])) < child_start + assert events.index(("end", destinations[2])) < child_start + + +@pytest.mark.asyncio +async def test_apply_entry_batch_flushes_before_and_after_mount_entries() -> None: + events: list[tuple[str, Path]] = [] + + async def mkdir(_path: Path) -> None: + return None + + async def exec_checked_nonzero(*_command: str) -> ExecResult: + return ExecResult(stdout=b"", stderr=b"", exit_code=0) + + async def apply_entry(_entry: object, dest: Path, _base_dir: Path) -> list[MaterializedFile]: + events.append(("start", dest)) + await asyncio.sleep(0) + events.append(("end", dest)) + return _materialized(dest) + + applier = ManifestApplier( + mkdir=mkdir, + exec_checked_nonzero=exec_checked_nonzero, + apply_entry=apply_entry, + ) + destinations = [ + Path("/workspace/alpha.txt"), + Path("/workspace/beta.txt"), + Path("/workspace/mount"), + Path("/workspace/gamma.txt"), + ] + + files = await applier._apply_entry_batch( + [ + (destinations[0], File(content=b"a")), + (destinations[1], File(content=b"b")), + (destinations[2], GCSMount(bucket="sandbox-bucket")), + (destinations[3], File(content=b"c")), + ], + base_dir=Path("/"), + ) + + assert [file.path for file in files] == destinations + mount_start = events.index(("start", destinations[2])) + gamma_start = events.index(("start", destinations[3])) + assert events.index(("end", destinations[0])) < mount_start + assert events.index(("end", destinations[1])) < mount_start + assert events.index(("end", destinations[2])) < gamma_start diff --git a/tests/test_sandbox_mounts.py b/tests/test_sandbox_mounts.py new file mode 100644 index 0000000000..737ca2fb31 --- /dev/null +++ b/tests/test_sandbox_mounts.py @@ -0,0 +1,107 @@ +from __future__ import annotations + +import io +import uuid +from pathlib import Path + +import pytest + +from agents.sandbox import Manifest +from agents.sandbox.entries import ( + AzureBlobMount, + GCSMount, + MountpointMountPattern, + RcloneMountPattern, +) +from agents.sandbox.entries.mounts.patterns import MountpointMountConfig, RcloneMountConfig +from agents.sandbox.session.base_sandbox_session import BaseSandboxSession +from agents.sandbox.session.sandbox_session_state import SandboxSessionState +from agents.sandbox.snapshot import NoopSnapshot +from agents.sandbox.types import ExecResult + + +class _MountConfigSession(BaseSandboxSession): + def __init__(self, *, session_id: uuid.UUID | None = None, config_text: str = "") -> None: + self.state = SandboxSessionState( + session_id=session_id or uuid.uuid4(), + manifest=Manifest(root="/workspace"), + snapshot=NoopSnapshot(id=str(uuid.uuid4())), + ) + self._config_text = config_text + + async def read(self, path: Path) -> io.BytesIO: + _ = path + return io.BytesIO(self._config_text.encode("utf-8")) + + async def shutdown(self) -> None: + return None + + async def write(self, path: Path, data: io.IOBase) -> None: + _ = (path, data) + raise AssertionError("write() should not be called in these tests") + + async def running(self) -> bool: + return True + + async def _exec_internal( + self, + *command: str | Path, + timeout: float | None = None, + ) -> ExecResult: + _ = (command, timeout) + raise AssertionError("exec() should not be called in these tests") + + async def persist_workspace(self) -> io.IOBase: + raise AssertionError("persist_workspace() should not be called in these tests") + + async def hydrate_workspace(self, data: io.IOBase) -> None: + _ = data + raise AssertionError("hydrate_workspace() should not be called in these tests") + + +@pytest.mark.asyncio +async def test_azure_blob_mount_builds_rclone_runtime_config_without_hidden_pattern_state() -> None: + session_id = uuid.uuid4() + pattern = RcloneMountPattern(config_file_path=Path("rclone.conf")) + remote_name = pattern.resolve_remote_name( + session_id=session_id.hex, + remote_kind="azureblob", + mount_type="azure_blob_mount", + ) + session = _MountConfigSession( + session_id=session_id, + config_text=f"[{remote_name}]\ntype = azureblob\n", + ) + mount = AzureBlobMount( + account="acct", + container="container", + mount_pattern=pattern, + ) + + apply_config = await mount._build_mount_config(session, pattern, include_config_text=True) + unmount_config = await mount._build_mount_config(session, pattern, include_config_text=False) + + assert isinstance(apply_config, RcloneMountConfig) + assert apply_config.remote_name == remote_name + assert apply_config.remote_path == "container" + assert apply_config.config_text is not None + assert "account = acct" in apply_config.config_text + assert isinstance(unmount_config, RcloneMountConfig) + assert unmount_config.remote_name == remote_name + assert unmount_config.config_text is None + + +@pytest.mark.asyncio +async def test_gcs_mount_uses_runtime_endpoint_override_without_mutating_pattern_options() -> None: + pattern = MountpointMountPattern() + mount = GCSMount(bucket="bucket", mount_pattern=pattern) + + config = await mount._build_mount_config( + _MountConfigSession(), + pattern, + include_config_text=False, + ) + + assert isinstance(config, MountpointMountConfig) + assert config.endpoint_url == "https://storage.googleapis.com" + assert pattern.options.endpoint_url is None diff --git a/tests/test_sandbox_retry.py b/tests/test_sandbox_retry.py new file mode 100644 index 0000000000..de43f3e98a --- /dev/null +++ b/tests/test_sandbox_retry.py @@ -0,0 +1,165 @@ +from __future__ import annotations + +import asyncio +from typing import cast + +import pytest + +from agents.sandbox.util.retry import ( + BackoffStrategy, + exception_chain_contains_type, + exception_chain_has_status_code, + iter_exception_chain, + retry_async, +) + + +class _ErrorWithHttpMetadata(Exception): + def __init__( + self, + message: str, + *, + status_code: int | None = None, + http_code: int | None = None, + response_status_code: int | None = None, + ) -> None: + super().__init__(message) + self.status_code = status_code + self.http_code = http_code + if response_status_code is not None: + self.response = type("_Response", (), {"status_code": response_status_code})() + + +def test_iter_exception_chain_supports_context_and_stops_on_cycles() -> None: + outer = RuntimeError("outer") + inner = ValueError("inner") + outer.__context__ = inner + + assert list(iter_exception_chain(outer)) == [outer, inner] + + cyclical_outer = RuntimeError("cyclical-outer") + cyclical_inner = ValueError("cyclical-inner") + cyclical_outer.__cause__ = cyclical_inner + cyclical_inner.__cause__ = cyclical_outer + + assert list(iter_exception_chain(cyclical_outer)) == [cyclical_outer, cyclical_inner] + + +def test_exception_chain_helpers_detect_types_and_status_codes() -> None: + outer = RuntimeError("outer") + inner = _ErrorWithHttpMetadata("inner", response_status_code=504) + outer.__cause__ = inner + + assert exception_chain_contains_type(outer, ()) is False + assert exception_chain_contains_type(outer, (_ErrorWithHttpMetadata,)) is True + assert exception_chain_contains_type(outer, (LookupError,)) is False + + assert exception_chain_has_status_code( + _ErrorWithHttpMetadata("status", status_code=500), + {500}, + ) + assert exception_chain_has_status_code( + _ErrorWithHttpMetadata("http", http_code=502), + {502}, + ) + assert exception_chain_has_status_code(outer, {504}) + assert exception_chain_has_status_code(outer, {503}) is False + + +def test_retry_async_validates_configuration() -> None: + with pytest.raises(ValueError, match="max_attempt must be >= 1"): + retry_async(max_attempt=0, retry_if=lambda _exc: True) + + with pytest.raises(ValueError, match="interval must be >= 0"): + retry_async(interval=-1, retry_if=lambda _exc: True) + + with pytest.raises(ValueError, match="backoff must be"): + retry_async( + backoff=cast(BackoffStrategy, "quadratic"), + retry_if=lambda _exc: True, + ) + + +@pytest.mark.parametrize( + ("backoff", "expected_delays"), + [ + (BackoffStrategy.FIXED, [0.5, 0.5]), + (BackoffStrategy.LINEAR, [0.5, 1.0]), + (BackoffStrategy.EXPONENTIAL, [0.5, 1.0]), + ], +) +@pytest.mark.asyncio +async def test_retry_async_retries_with_expected_backoff_and_async_hook( + monkeypatch: pytest.MonkeyPatch, + backoff: BackoffStrategy, + expected_delays: list[float], +) -> None: + sleep_delays: list[float] = [] + hook_calls: list[tuple[int, int, float]] = [] + attempts = 0 + + async def fake_sleep(delay: float) -> None: + sleep_delays.append(delay) + + async def on_retry( + _exc: Exception, + attempt: int, + max_attempt: int, + delay_s: float, + *_args: object, + **_kwargs: object, + ) -> None: + hook_calls.append((attempt, max_attempt, delay_s)) + + monkeypatch.setattr(asyncio, "sleep", fake_sleep) + + @retry_async( + interval=0.5, + max_attempt=3, + backoff=backoff, + retry_if=lambda exc, *_args, **_kwargs: isinstance(exc, RuntimeError), + on_retry=on_retry, + ) + async def flaky(label: str) -> str: + nonlocal attempts + attempts += 1 + if attempts < 3: + raise RuntimeError(label) + return f"ok:{label}" + + result = await flaky("sandbox") + + assert result == "ok:sandbox" + assert attempts == 3 + assert sleep_delays == expected_delays + assert hook_calls == [(1, 3, expected_delays[0]), (2, 3, expected_delays[1])] + assert str(backoff) == backoff.value + + +@pytest.mark.asyncio +async def test_retry_async_stops_without_sleep_when_retry_is_rejected( + monkeypatch: pytest.MonkeyPatch, +) -> None: + attempts = 0 + + async def fail_sleep(_delay: float) -> None: + raise AssertionError("sleep should not be called") + + monkeypatch.setattr(asyncio, "sleep", fail_sleep) + + @retry_async( + interval=0.5, + max_attempt=3, + backoff=BackoffStrategy.EXPONENTIAL, + retry_if=lambda _exc, *_args, **_kwargs: False, + on_retry=lambda *_args, **_kwargs: None, + ) + async def always_fail() -> None: + nonlocal attempts + attempts += 1 + raise RuntimeError("stop") + + with pytest.raises(RuntimeError, match="stop"): + await always_fail() + + assert attempts == 1 diff --git a/tests/test_sandbox_runtime.py b/tests/test_sandbox_runtime.py new file mode 100644 index 0000000000..9909bb2964 --- /dev/null +++ b/tests/test_sandbox_runtime.py @@ -0,0 +1,2859 @@ +from __future__ import annotations + +import asyncio +import io +import json +import os +import shutil +import sys +import tempfile +import uuid +from pathlib import Path +from typing import Any, Literal, TypedDict, cast + +import pytest +from openai.types.responses.response_output_item import LocalShellCall, LocalShellCallAction +from openai.types.responses.response_reasoning_item import ResponseReasoningItem, Summary + +from agents import Agent, AgentHooks, LocalShellTool, RunHooks, Runner, function_tool +from agents.exceptions import UserError +from agents.guardrail import GuardrailFunctionOutput, OutputGuardrail +from agents.items import ModelResponse, ToolCallOutputItem, TResponseInputItem +from agents.model_settings import ModelSettings +from agents.prompts import GenerateDynamicPromptData, Prompt +from agents.run import CallModelData, ModelInputData, RunConfig +from agents.run_context import AgentHookContext, RunContextWrapper +from agents.run_state import RunState, _build_agent_identity_map +from agents.sandbox import Manifest, SandboxAgent, SandboxRunConfig +from agents.sandbox.capabilities import Capability +from agents.sandbox.entries import File +from agents.sandbox.errors import ExecNonZeroError, ExecTransportError, InvalidManifestPathError +from agents.sandbox.runtime import SandboxRuntime +from agents.sandbox.runtime_session_manager import SandboxRuntimeSessionManager +from agents.sandbox.sandboxes import unix_local as unix_local_module +from agents.sandbox.sandboxes.unix_local import ( + UnixLocalSandboxClient, + UnixLocalSandboxSession, + UnixLocalSandboxSessionState, +) +from agents.sandbox.session.base_sandbox_session import BaseSandboxSession +from agents.sandbox.session.sandbox_client import BaseSandboxClient +from agents.sandbox.session.sandbox_session import SandboxSession +from agents.sandbox.session.sandbox_session_state import SandboxSessionState +from agents.sandbox.snapshot import LocalSnapshotSpec, NoopSnapshot, SnapshotBase +from agents.sandbox.types import ExecResult, User +from agents.stream_events import RunItemStreamEvent +from agents.tool import Tool +from tests.fake_model import FakeModel +from tests.test_responses import ( + get_final_output_message, + get_function_tool, + get_function_tool_call, + get_handoff_tool_call, +) +from tests.utils.simple_session import SimpleListSession + + +class _FakeSession(BaseSandboxSession): + def __init__( + self, + manifest: Manifest, + *, + start_gate: asyncio.Event | None = None, + ) -> None: + self.state = SandboxSessionState( + manifest=manifest, + snapshot=NoopSnapshot(id=str(uuid.uuid4())), + ) + self._start_gate = start_gate + self._running = False + self.start_calls = 0 + self.stop_calls = 0 + self.shutdown_calls = 0 + self.close_dependency_calls = 0 + + async def start(self) -> None: + self.start_calls += 1 + if self._start_gate is not None: + await self._start_gate.wait() + self._running = True + + async def stop(self) -> None: + self.stop_calls += 1 + self._running = False + + async def shutdown(self) -> None: + self.shutdown_calls += 1 + + async def running(self) -> bool: + return self._running + + async def read(self, path: Path) -> io.BytesIO: + _ = path + raise AssertionError("read() should not be called in these tests") + + async def write(self, path: Path, data: io.IOBase) -> None: + _ = (path, data) + raise AssertionError("write() should not be called in these tests") + + async def _exec_internal( + self, + *command: str | Path, + timeout: float | None = None, + ) -> ExecResult: + _ = (command, timeout) + raise AssertionError("exec() should not be called in these tests") + + async def persist_workspace(self) -> io.IOBase: + return io.BytesIO() + + async def hydrate_workspace(self, data: io.IOBase) -> None: + _ = data + + async def _aclose_dependencies(self) -> None: + self.close_dependency_calls += 1 + await super()._aclose_dependencies() + + +class _FailingStopSession(_FakeSession): + async def stop(self) -> None: + await super().stop() + raise RuntimeError("stop failed") + + +class _BlockingStopSession(_FakeSession): + def __init__(self, manifest: Manifest, stop_gate: asyncio.Event) -> None: + super().__init__(manifest) + self._stop_gate = stop_gate + + async def stop(self) -> None: + await super().stop() + await self._stop_gate.wait() + + +class _MarkerSnapshot(SnapshotBase): + __test__ = False + type: Literal["marker"] = "marker" + marker: str = "initial" + + async def persist(self, data: io.IOBase) -> None: + _ = data + + async def restore(self) -> io.IOBase: + return io.BytesIO() + + async def restorable(self) -> bool: + return False + + +class _PersistingStopSession(_BlockingStopSession): + def __init__(self, manifest: Manifest, stop_gate: asyncio.Event) -> None: + super().__init__(manifest, stop_gate) + self.state.snapshot = _MarkerSnapshot(id="marker") + + async def stop(self) -> None: + self.stop_calls += 1 + self._running = False + await self._stop_gate.wait() + snapshot = cast(_MarkerSnapshot, self.state.snapshot) + snapshot.marker = "persisted" + + +class _ProvisioningFailureSession(_FakeSession): + async def _exec_internal( + self, + *command: str | Path, + timeout: float | None = None, + ) -> ExecResult: + _ = timeout + cmd = [str(part) for part in command] + if cmd[:2] == ["mkdir", "-p"]: + return ExecResult(stdout=b"", stderr=b"", exit_code=0) + if cmd and cmd[0] in {"groupadd", "useradd"}: + return ExecResult(stdout=b"", stderr=f"missing {cmd[0]}".encode(), exit_code=1) + return ExecResult(stdout=b"", stderr=b"", exit_code=0) + + +class _RestorableSnapshot(SnapshotBase): + __test__ = False + type: Literal["restorable"] = "restorable" + + async def persist(self, data: io.IOBase) -> None: + _ = data + + async def restore(self) -> io.IOBase: + return io.BytesIO(b"snapshot") + + async def restorable(self) -> bool: + return True + + +class _RestorableProvisioningFailureSession(_ProvisioningFailureSession): + def __init__(self, manifest: Manifest, *, provision_on_resume: bool = True) -> None: + super().__init__(manifest) + self.state.snapshot = _RestorableSnapshot(id="resume") + self.cleared_workspace_root = False + self.hydrate_calls = 0 + self._provision_on_resume = provision_on_resume + + async def start(self) -> None: + self.start_calls += 1 + self._running = True + await BaseSandboxSession.start(self) + + async def hydrate_workspace(self, data: io.IOBase) -> None: + _ = data + self.hydrate_calls += 1 + + async def _clear_workspace_root_on_resume(self) -> None: + self.cleared_workspace_root = True + + def should_provision_manifest_accounts_on_resume(self) -> bool: + return self._provision_on_resume + + +@pytest.mark.asyncio +async def test_sandbox_session_aclose_runs_public_cleanup_lifecycle() -> None: + inner = _FakeSession(Manifest()) + session = SandboxSession(inner) + + await session.aclose() + + assert inner.stop_calls == 1 + assert inner.shutdown_calls == 1 + assert inner.close_dependency_calls == 1 + + +@pytest.mark.asyncio +async def test_sandbox_session_aclose_closes_dependencies_when_stop_fails() -> None: + inner = _FailingStopSession(Manifest()) + session = SandboxSession(inner) + + with pytest.raises(RuntimeError, match="stop failed"): + await session.aclose() + + assert inner.stop_calls == 1 + assert inner.shutdown_calls == 0 + assert inner.close_dependency_calls == 1 + + +def _extract_user_text(item: dict[str, object]) -> str: + content = item["content"] + if isinstance(content, str): + return content + if isinstance(content, list): + first = content[0] + if isinstance(first, dict): + return str(first.get("text", "")) + raise AssertionError(f"Unexpected content payload: {content!r}") + + +def _get_reasoning_item() -> ResponseReasoningItem: + return ResponseReasoningItem( + id="rid", + type="reasoning", + summary=[Summary(text="thinking", type="summary_text")], + ) + + +class _CreateKwargs(TypedDict): + snapshot: object | None + manifest: Manifest | None + options: dict[str, str] + + +class _FakeClient(BaseSandboxClient[dict[str, str]]): + backend_id = "fake" + + def __init__(self, session: _FakeSession) -> None: + self.inner_session = session + self.session = self._wrap_session(session) + self.create_kwargs: _CreateKwargs | None = None + self.resume_state: SandboxSessionState | None = None + self.delete_calls = 0 + + async def create( + self, + *, + snapshot: object | None = None, + manifest: Manifest | None = None, + options: dict[str, str], + ) -> SandboxSession: + self.create_kwargs = { + "snapshot": snapshot, + "manifest": manifest, + "options": options, + } + if manifest is not None: + self.inner_session.state.manifest = manifest + return self.session + + async def delete(self, session: SandboxSession) -> SandboxSession: + self.delete_calls += 1 + return session + + async def resume(self, state: SandboxSessionState) -> SandboxSession: + self.resume_state = state + self.inner_session.state = state + return self.session + + def deserialize_session_state(self, payload: dict[str, object]) -> SandboxSessionState: + return SandboxSessionState.model_validate(payload) + + +class _ManifestSessionClient(BaseSandboxClient[None]): + backend_id = "manifest" + supports_default_options = True + + def __init__(self) -> None: + self.created_manifests: list[Manifest | None] = [] + + async def create( + self, + *, + snapshot: object | None = None, + manifest: Manifest | None = None, + options: None = None, + ) -> SandboxSession: + _ = (snapshot, options) + self.created_manifests.append(manifest) + assert manifest is not None + session = _FakeSession(manifest) + return self._wrap_session(session) + + async def delete(self, session: SandboxSession) -> SandboxSession: + return session + + async def resume(self, state: SandboxSessionState) -> SandboxSession: + return self._wrap_session(_FakeSession(state.manifest)) + + def deserialize_session_state(self, payload: dict[str, object]) -> SandboxSessionState: + return SandboxSessionState.model_validate(payload) + + +class _RecordingCapability(Capability): + type: str = "recording" + bound_session: BaseSandboxSession | None = None + instruction_text: str | None = None + provided_tools: list[Tool] + + def __init__( + self, + *, + instruction_text: str | None = None, + provided_tools: list[Tool] | None = None, + ) -> None: + super().__init__(type="recording") + self.bound_session = None + self.instruction_text = instruction_text + self.provided_tools = list(provided_tools or []) + + def bind(self, session: BaseSandboxSession) -> None: + self.bound_session = session + + def tools(self) -> list[Tool]: + return list(self.provided_tools) + + async def instructions(self, manifest: Manifest) -> str | None: + _ = manifest + return self.instruction_text + + +class _NestedStateCapability(Capability): + type: str = "nested-state" + state: dict[str, list[str]] + + def __init__(self) -> None: + super().__init__(type="nested-state") + self.state = {"seen": []} + + +class _NestedObjectState: + def __init__(self) -> None: + self.seen: list[str] = [] + + +class _NestedObjectCapability(Capability): + type: str = "nested-object-state" + state: _NestedObjectState + + def __init__(self) -> None: + super().__init__(type="nested-object-state") + self.state = _NestedObjectState() + + +class _AwaitableSessionCapability(Capability): + type: str = "awaitable-session" + bound_session: BaseSandboxSession | None = None + release_gate: asyncio.Event + first_instruction_started: asyncio.Event + second_instruction_started: asyncio.Event + + def __init__( + self, + *, + release_gate: asyncio.Event, + first_instruction_started: asyncio.Event, + second_instruction_started: asyncio.Event, + ) -> None: + super().__init__(type="awaitable-session") + self.bound_session = None + self.release_gate = release_gate + self.first_instruction_started = first_instruction_started + self.second_instruction_started = second_instruction_started + + def bind(self, session: BaseSandboxSession) -> None: + self.bound_session = session + + async def instructions(self, manifest: Manifest) -> str | None: + _ = manifest + assert self.bound_session is not None + readme = self.bound_session.state.manifest.entries["README.md"] + assert isinstance(readme, File) + readme_text = readme.content.decode() + if readme_text == "Session one instructions.": + self.first_instruction_started.set() + elif readme_text == "Session two instructions.": + self.second_instruction_started.set() + await self.release_gate.wait() + return readme_text + + +class _ManifestInstructionsCapability(Capability): + type: str = "manifest-instructions" + bound_session: BaseSandboxSession | None = None + + def __init__(self) -> None: + super().__init__(type="manifest-instructions") + self.bound_session = None + + def bind(self, session: BaseSandboxSession) -> None: + self.bound_session = session + + async def instructions(self, manifest: Manifest) -> str | None: + _ = manifest + assert self.bound_session is not None + readme = self.bound_session.state.manifest.entries["README.md"] + assert isinstance(readme, File) + return readme.content.decode() + + +class _ManifestMutationCapability(Capability): + type: str = "manifest-mutation" + + def __init__(self, *, rel_path: str = "cap.txt", content: bytes = b"capability") -> None: + super().__init__(type="manifest-mutation") + self.rel_path = rel_path + self.content = content + + def process_manifest(self, manifest: Manifest) -> Manifest: + manifest.entries[self.rel_path] = File(content=self.content) + return manifest + + +class _SessionFileCapability(Capability): + type: str = "session-files" + bound_session: BaseSandboxSession | None = None + + def __init__(self) -> None: + super().__init__(type="session-files") + self.bound_session = None + + def bind(self, session: BaseSandboxSession) -> None: + self.bound_session = session + + def tools(self) -> list[Tool]: + @function_tool(name_override="write_file") + async def write_file(path: str, content: str) -> str: + assert self.bound_session is not None + await self.bound_session.write(Path(path), io.BytesIO(content.encode("utf-8"))) + return "wrote" + + @function_tool(name_override="read_file") + async def read_file(path: str) -> str: + assert self.bound_session is not None + data = await self.bound_session.read(Path(path)) + return cast(bytes, data.read()).decode("utf-8") + + return [write_file, read_file] + + +class _RecordingRunHooks(RunHooks[None]): + def __init__(self) -> None: + self.started_agents: list[Agent[None]] = [] + self.ended_agents: list[Agent[None]] = [] + self.llm_started_agents: list[Agent[None]] = [] + self.llm_ended_agents: list[Agent[None]] = [] + + async def on_agent_start(self, context: AgentHookContext[None], agent: Agent[None]) -> None: + _ = context + self.started_agents.append(agent) + + async def on_llm_start( + self, + context: RunContextWrapper[None], + agent: Agent[None], + system_prompt: str | None, + input_items: list[TResponseInputItem], + ) -> None: + _ = (context, system_prompt, input_items) + self.llm_started_agents.append(agent) + + async def on_llm_end( + self, + context: RunContextWrapper[None], + agent: Agent[None], + response: ModelResponse, + ) -> None: + _ = (context, response) + self.llm_ended_agents.append(agent) + + async def on_agent_end( + self, + context: AgentHookContext[None], + agent: Agent[None], + output: object, + ) -> None: + _ = (context, output) + self.ended_agents.append(agent) + + +class _RecordingAgentHooks(AgentHooks[None]): + def __init__(self) -> None: + self.started_agents: list[Agent[None]] = [] + self.ended_agents: list[Agent[None]] = [] + self.llm_started_agents: list[Agent[None]] = [] + self.llm_ended_agents: list[Agent[None]] = [] + + async def on_start(self, context: AgentHookContext[None], agent: Agent[None]) -> None: + _ = context + self.started_agents.append(agent) + + async def on_llm_start( + self, + context: RunContextWrapper[None], + agent: Agent[None], + system_prompt: str | None, + input_items: list[TResponseInputItem], + ) -> None: + _ = (context, system_prompt, input_items) + self.llm_started_agents.append(agent) + + async def on_llm_end( + self, + context: RunContextWrapper[None], + agent: Agent[None], + response: ModelResponse, + ) -> None: + _ = (context, response) + self.llm_ended_agents.append(agent) + + async def on_end( + self, + context: AgentHookContext[None], + agent: Agent[None], + output: object, + ) -> None: + _ = (context, output) + self.ended_agents.append(agent) + + +def _sandbox_run_config(client: _FakeClient | None = None) -> RunConfig: + return RunConfig( + sandbox=SandboxRunConfig( + client=client, + options={"image": "sandbox"} if client is not None else None, + ) + ) + + +@pytest.mark.asyncio +async def test_runner_merges_sandbox_instructions_and_tools() -> None: + model = FakeModel(initial_output=[get_final_output_message("done")]) + capability_tool = get_function_tool("capability_tool", "ok") + capability = _RecordingCapability( + instruction_text="Capability instructions.", + provided_tools=[capability_tool], + ) + manifest = Manifest(entries={"README.md": File(content=b"Follow the repo contract.")}) + session = _FakeSession(manifest) + client = _FakeClient(session) + agent = SandboxAgent( + name="sandbox", + model=model, + instructions="Base instructions.", + developer_instructions="Developer instructions.", + default_manifest=manifest, + capabilities=[capability], + ) + + result = await Runner.run( + agent, + "hello", + run_config=_sandbox_run_config(client), + ) + + assert result.final_output == "done" + assert capability.bound_session is None + assert session.start_calls == 1 + assert session.stop_calls == 1 + assert session.shutdown_calls == 1 + assert session.close_dependency_calls == 1 + assert client.delete_calls == 1 + + state = result.to_state() + assert state._sandbox is not None + assert state._sandbox["backend_id"] == "fake" + assert state._sandbox["current_agent_name"] == agent.name + assert state._sandbox["current_agent_key"] == agent.name + sessions_by_agent = state._sandbox["sessions_by_agent"] + assert isinstance(sessions_by_agent, dict) + assert sessions_by_agent[agent.name] == { + "agent_name": agent.name, + "session_state": state._sandbox["session_state"], + } + + assert client.create_kwargs is not None + assert client.create_kwargs["manifest"] is not manifest + assert client.create_kwargs["options"] == {"image": "sandbox"} + assert isinstance(client.create_kwargs["snapshot"], LocalSnapshotSpec) + + assert model.first_turn_args is not None + assert model.first_turn_args["system_instructions"] == ( + "Base instructions.\n\nDeveloper instructions.\n\nCapability instructions." + ) + assert [tool.name for tool in model.first_turn_args["tools"]] == ["capability_tool"] + + input_items = model.first_turn_args["input"] + assert isinstance(input_items, list) + assert _extract_user_text(input_items[0]) == "hello" + + +@pytest.mark.asyncio +async def test_runner_requires_sandbox_config_for_sandbox_agent() -> None: + agent = SandboxAgent( + name="sandbox", + model=FakeModel(initial_output=[get_final_output_message("done")]), + instructions="Base instructions.", + ) + + with pytest.raises(UserError, match="RunConfig\\(sandbox=.*\\)"): + await Runner.run(agent, "hello") + + +@pytest.mark.asyncio +async def test_runner_streamed_cleans_runner_owned_session() -> None: + model = FakeModel(initial_output=[get_final_output_message("done")]) + session = _FakeSession(Manifest()) + client = _FakeClient(session) + agent = SandboxAgent( + name="sandbox", + model=model, + instructions="Base instructions.", + ) + + result = Runner.run_streamed( + agent, + "hello", + run_config=_sandbox_run_config(client), + ) + events = [event async for event in result.stream_events()] + + assert events + assert result.final_output == "done" + assert session.start_calls == 1 + assert session.stop_calls == 1 + assert session.shutdown_calls == 1 + assert session.close_dependency_calls == 1 + assert client.delete_calls == 1 + + state = result.to_state() + assert state._sandbox is not None + assert state._sandbox["backend_id"] == "fake" + assert state._sandbox["current_agent_name"] == agent.name + assert state._sandbox["current_agent_key"] == agent.name + sessions_by_agent = state._sandbox["sessions_by_agent"] + assert isinstance(sessions_by_agent, dict) + assert sessions_by_agent[agent.name] == { + "agent_name": agent.name, + "session_state": state._sandbox["session_state"], + } + + +@pytest.mark.asyncio +async def test_runner_does_not_close_injected_sandbox_session() -> None: + model = FakeModel(initial_output=[get_final_output_message("done")]) + default_manifest = Manifest(entries={"default.txt": File(content=b"default")}) + session_manifest = Manifest(entries={"session.txt": File(content=b"session")}) + injected_session = _FakeSession(session_manifest) + agent = SandboxAgent( + name="sandbox", + model=model, + instructions="Base instructions.", + default_manifest=default_manifest, + ) + + result = await Runner.run( + agent, + "hello", + run_config=RunConfig( + sandbox=SandboxRunConfig( + session=injected_session, + manifest=Manifest(entries={"override.txt": File(content=b"override")}), + ) + ), + ) + + assert result.final_output == "done" + assert injected_session.start_calls == 1 + assert injected_session.stop_calls == 0 + assert injected_session.shutdown_calls == 0 + assert injected_session.close_dependency_calls == 0 + + assert model.first_turn_args is not None + input_items = model.first_turn_args["input"] + assert isinstance(input_items, str) or isinstance(input_items, list) + assert injected_session.state.manifest.entries == session_manifest.entries + + +@pytest.mark.asyncio +async def test_runner_does_not_restart_running_injected_sandbox_session() -> None: + model = FakeModel(initial_output=[get_final_output_message("done")]) + injected_session = _FakeSession(Manifest(entries={"session.txt": File(content=b"session")})) + injected_session._running = True + agent = SandboxAgent( + name="sandbox", + model=model, + instructions="Base instructions.", + ) + + result = await Runner.run( + agent, + "hello", + run_config=RunConfig(sandbox=SandboxRunConfig(session=injected_session)), + ) + + assert result.final_output == "done" + assert injected_session.start_calls == 0 + assert injected_session.stop_calls == 0 + assert injected_session.shutdown_calls == 0 + + +@pytest.mark.asyncio +async def test_runner_uses_public_sandbox_agent_for_dynamic_instructions() -> None: + model = FakeModel(initial_output=[get_final_output_message("done")]) + session = _FakeSession(Manifest()) + client = _FakeClient(session) + seen_agents: list[Agent[Any]] = [] + + def dynamic_instructions(_ctx: RunContextWrapper[Any], current_agent: Agent[Any]) -> str: + seen_agents.append(current_agent) + return "Saw public agent." if current_agent is agent else "Saw execution clone." + + agent = SandboxAgent( + name="sandbox", + model=model, + instructions=dynamic_instructions, + capabilities=[ + _RecordingCapability( + instruction_text="Capability instructions.", + provided_tools=[get_function_tool("capability_tool", "ok")], + ) + ], + ) + + result = await Runner.run(agent, "hello", run_config=_sandbox_run_config(client)) + + assert result.final_output == "done" + assert seen_agents == [agent] + assert model.first_turn_args is not None + assert model.first_turn_args["system_instructions"] == ( + "Saw public agent.\n\nCapability instructions." + ) + + +@pytest.mark.asyncio +async def test_runner_uses_public_sandbox_agent_for_dynamic_prompts() -> None: + seen_agents: list[Agent[Any]] = [] + + def dynamic_prompt(data: GenerateDynamicPromptData) -> Prompt: + seen_agents.append(data.agent) + return {"id": "prompt_test", "variables": {"agent_name": data.agent.name}} + + agent = SandboxAgent( + name="sandbox", + model=FakeModel(initial_output=[get_final_output_message("done")]), + instructions="Base instructions.", + prompt=dynamic_prompt, + capabilities=[_RecordingCapability(instruction_text="Capability instructions.")], + ) + + result = await Runner.run( + agent, "hello", run_config=_sandbox_run_config(_FakeClient(_FakeSession(Manifest()))) + ) + + assert result.final_output == "done" + assert seen_agents == [agent] + + streamed_agent = SandboxAgent( + name="streamed-sandbox", + model=FakeModel(initial_output=[get_final_output_message("streamed done")]), + instructions="Base instructions.", + prompt=dynamic_prompt, + capabilities=[_RecordingCapability(instruction_text="Capability instructions.")], + ) + streamed = Runner.run_streamed( + streamed_agent, + "hello", + run_config=_sandbox_run_config(_FakeClient(_FakeSession(Manifest()))), + ) + async for _ in streamed.stream_events(): + pass + + assert streamed.final_output == "streamed done" + assert seen_agents == [agent, streamed_agent] + + +@pytest.mark.asyncio +async def test_runner_uses_public_agent_for_call_model_input_filter() -> None: + seen_agents: list[Agent[Any]] = [] + + def capture_model_input(data: CallModelData[Any]) -> ModelInputData: + seen_agents.append(data.agent) + return data.model_data + + agent = SandboxAgent( + name="sandbox", + model=FakeModel(initial_output=[get_final_output_message("done")]), + instructions="Base instructions.", + capabilities=[_RecordingCapability(instruction_text="Capability instructions.")], + ) + + result = await Runner.run( + agent, + "hello", + run_config=RunConfig( + sandbox=SandboxRunConfig( + client=_FakeClient(_FakeSession(Manifest())), + options={"image": "sandbox"}, + ), + call_model_input_filter=capture_model_input, + ), + ) + + assert result.final_output == "done" + assert seen_agents == [agent] + + +@pytest.mark.asyncio +async def test_runner_streamed_uses_public_agent_for_call_model_input_filter() -> None: + seen_agents: list[Agent[Any]] = [] + + def capture_model_input(data: CallModelData[Any]) -> ModelInputData: + seen_agents.append(data.agent) + return data.model_data + + agent = SandboxAgent( + name="sandbox", + model=FakeModel(initial_output=[get_final_output_message("done")]), + instructions="Base instructions.", + capabilities=[_RecordingCapability(instruction_text="Capability instructions.")], + ) + + result = Runner.run_streamed( + agent, + "hello", + run_config=RunConfig( + sandbox=SandboxRunConfig( + client=_FakeClient(_FakeSession(Manifest())), + options={"image": "sandbox"}, + ), + call_model_input_filter=capture_model_input, + ), + ) + events = [event async for event in result.stream_events()] + + assert events + assert result.final_output == "done" + assert seen_agents == [agent] + + +@pytest.mark.asyncio +async def test_runner_reuses_prepared_sandbox_agent_across_turns_for_tool_choice_reset() -> None: + model = FakeModel() + tool = get_function_tool("capability_tool", "ok") + model.add_multiple_turn_outputs( + [ + [get_function_tool_call("capability_tool", json.dumps({}))], + [get_final_output_message("done")], + ] + ) + session = _FakeSession(Manifest()) + client = _FakeClient(session) + agent = SandboxAgent( + name="sandbox", + model=model, + instructions="Base instructions.", + tools=[tool], + model_settings=ModelSettings(tool_choice="required"), + ) + + result = await Runner.run(agent, "hello", run_config=_sandbox_run_config(client)) + + assert result.final_output == "done" + assert model.first_turn_args is not None + assert model.first_turn_args["model_settings"].tool_choice == "required" + assert model.last_turn_args["model_settings"].tool_choice is None + + +@pytest.mark.asyncio +async def test_runner_rebuilds_sandbox_resources_for_handoff_target_agent() -> None: + triage_model = FakeModel() + worker_model = FakeModel(initial_output=[get_final_output_message("done")]) + client = _ManifestSessionClient() + triage_manifest = Manifest(entries={"README.md": File(content=b"Triage workspace")}) + worker_manifest = Manifest(entries={"README.md": File(content=b"Worker workspace")}) + worker = SandboxAgent( + name="worker", + model=worker_model, + instructions="Worker instructions.", + default_manifest=worker_manifest, + capabilities=[_ManifestInstructionsCapability()], + ) + triage = SandboxAgent( + name="triage", + model=triage_model, + instructions="Triage instructions.", + default_manifest=triage_manifest, + capabilities=[_ManifestInstructionsCapability()], + handoffs=[worker], + ) + triage_model.turn_outputs = [[get_handoff_tool_call(worker)]] + + result = await Runner.run( + triage, + "route this", + run_config=RunConfig(sandbox=SandboxRunConfig(client=client)), + ) + + assert result.final_output == "done" + assert len(client.created_manifests) == 2 + assert client.created_manifests[0] is not None + assert client.created_manifests[1] is not None + assert ( + client.created_manifests[0].entries["README.md"] + != client.created_manifests[1].entries["README.md"] + ) + assert worker_model.first_turn_args is not None + assert worker_model.first_turn_args["system_instructions"] == ( + "Worker instructions.\n\nWorker workspace" + ) + + +@pytest.mark.asyncio +async def test_runner_resumed_handoff_materializes_manifest_for_new_sandbox_agent() -> None: + triage_model = FakeModel() + worker_model = FakeModel(initial_output=[get_final_output_message("done")]) + client = _ManifestSessionClient() + + @function_tool(name_override="approval_tool", needs_approval=True) + def approval_tool() -> str: + return "approved" + + triage_manifest = Manifest(entries={"README.md": File(content=b"Triage workspace")}) + worker_manifest = Manifest(entries={"README.md": File(content=b"Worker workspace")}) + worker = SandboxAgent( + name="worker", + model=worker_model, + instructions="Worker instructions.", + default_manifest=worker_manifest, + capabilities=[_ManifestInstructionsCapability()], + ) + triage = SandboxAgent( + name="triage", + model=triage_model, + instructions="Triage instructions.", + default_manifest=triage_manifest, + tools=[approval_tool], + capabilities=[_ManifestInstructionsCapability()], + handoffs=[worker], + ) + triage_model.add_multiple_turn_outputs( + [ + [get_function_tool_call("approval_tool", json.dumps({}), call_id="call_resume")], + [get_handoff_tool_call(worker)], + ] + ) + + first_run = await Runner.run( + triage, + "route this", + run_config=RunConfig(sandbox=SandboxRunConfig(client=client)), + ) + + assert len(first_run.interruptions) == 1 + state = first_run.to_state() + state.approve(first_run.interruptions[0]) + + resumed = await Runner.run( + triage, + state, + run_config=RunConfig(sandbox=SandboxRunConfig(client=client)), + ) + + assert resumed.final_output == "done" + assert len(client.created_manifests) == 2 + assert client.created_manifests[1] is not None + assert worker_model.first_turn_args is not None + assert worker_model.first_turn_args["system_instructions"] == ( + "Worker instructions.\n\nWorker workspace" + ) + + +@pytest.mark.asyncio +async def test_unix_local_client_rewrites_default_manifest_root_to_temp_workspace() -> None: + client = UnixLocalSandboxClient() + manifest = Manifest(entries={"default.txt": File(content=b"default")}) + + session = await client.create(manifest=manifest, options=None) + workspace_root = Path(session.state.manifest.root) + try: + session_manifest = session.state.manifest + session_state = cast(UnixLocalSandboxSessionState, session.state) + + assert session_manifest is not manifest + assert session_manifest.entries == manifest.entries + assert session_manifest.root != manifest.root + assert workspace_root.is_absolute() + assert workspace_root.name.startswith("uc-local-") + assert session_state.workspace_root_owned is True + assert manifest.root == "/workspace" + finally: + await client.delete(session) + assert not workspace_root.exists() + + +@pytest.mark.asyncio +async def test_runner_allows_fresh_unix_local_sessions_without_options() -> None: + agent = SandboxAgent( + name="sandbox", + model=FakeModel(initial_output=[get_final_output_message("done")]), + instructions="Base instructions.", + ) + + result = await Runner.run( + agent, + "hello", + run_config=RunConfig(sandbox=SandboxRunConfig(client=UnixLocalSandboxClient())), + ) + + assert result.final_output == "done" + + +@pytest.mark.asyncio +async def test_unix_local_client_delete_preserves_caller_owned_workspace_root() -> None: + client = UnixLocalSandboxClient() + workspace_root = Path(tempfile.mkdtemp(prefix="caller-owned-")) + manifest = Manifest(root=str(workspace_root)) + + session = await client.create(manifest=manifest, options=None) + assert cast(UnixLocalSandboxSessionState, session.state).workspace_root_owned is False + + await client.delete(session) + + assert workspace_root.exists() + shutil.rmtree(workspace_root) + + +@pytest.mark.asyncio +async def test_unix_local_runner_cleanup_preserves_resumed_caller_owned_workspace_root() -> None: + workspace_root = Path(tempfile.mkdtemp(prefix="resumed-owned-")) + state = UnixLocalSandboxSessionState( + session_id=uuid.uuid4(), + manifest=Manifest(root=str(workspace_root)), + snapshot=NoopSnapshot(id=str(uuid.uuid4())), + ) + agent = SandboxAgent( + name="sandbox", + model=FakeModel(initial_output=[get_final_output_message("done")]), + instructions="Base instructions.", + ) + + try: + result = await Runner.run( + agent, + "hello", + run_config=RunConfig( + sandbox=SandboxRunConfig( + client=UnixLocalSandboxClient(), + session_state=state, + ) + ), + ) + finally: + assert workspace_root.exists() + shutil.rmtree(workspace_root) + + assert result.final_output == "done" + + +@pytest.mark.asyncio +async def test_unix_local_read_and_write_reject_paths_outside_workspace_root() -> None: + client = UnixLocalSandboxClient() + workspace_root = Path(tempfile.mkdtemp(prefix="workspace-root-")) + session = await client.create(manifest=Manifest(root=str(workspace_root)), options=None) + + try: + with pytest.raises(InvalidManifestPathError, match="must not escape root"): + await session.write(Path("../secret.txt"), io.BytesIO(b"nope")) + with pytest.raises(InvalidManifestPathError, match="must not escape root"): + await session.read(Path("../secret.txt")) + finally: + await client.delete(session) + shutil.rmtree(workspace_root) + + +@pytest.mark.asyncio +async def test_runner_streamed_ignores_sandbox_cleanup_failures_after_success() -> None: + session = _FailingStopSession(Manifest()) + client = _FakeClient(session) + agent = SandboxAgent( + name="sandbox", + model=FakeModel(initial_output=[get_final_output_message("done")]), + instructions="Base instructions.", + ) + + result = Runner.run_streamed(agent, "hello", run_config=_sandbox_run_config(client)) + events = [event async for event in result.stream_events()] + + assert events + assert result.final_output == "done" + assert result._sandbox_session is None + + +@pytest.mark.asyncio +async def test_runner_omits_sandbox_resume_state_when_cleanup_fails() -> None: + session = _FailingStopSession(Manifest()) + client = _FakeClient(session) + agent = SandboxAgent( + name="sandbox", + model=FakeModel(initial_output=[get_final_output_message("done")]), + instructions="Base instructions.", + ) + + result = await Runner.run(agent, "hello", run_config=_sandbox_run_config(client)) + state = result.to_state() + + assert result.final_output == "done" + assert result._sandbox_resume_state is None + assert result._sandbox_session is None + assert state._sandbox is None + + +@pytest.mark.asyncio +async def test_runner_clears_sandbox_session_from_non_streamed_results_after_cleanup() -> None: + session = _FakeSession(Manifest()) + client = _FakeClient(session) + agent = SandboxAgent( + name="sandbox", + model=FakeModel(initial_output=[get_final_output_message("done")]), + instructions="Base instructions.", + ) + + result = await Runner.run(agent, "hello", run_config=_sandbox_run_config(client)) + + assert result.final_output == "done" + assert result._sandbox_session is None + + +@pytest.mark.asyncio +async def test_runner_streamed_cleans_sandbox_once_after_stream_completion() -> None: + session = _FakeSession(Manifest()) + client = _FakeClient(session) + agent = SandboxAgent( + name="sandbox", + model=FakeModel(initial_output=[get_final_output_message("done")]), + instructions="Base instructions.", + ) + + result = Runner.run_streamed(agent, "hello", run_config=_sandbox_run_config(client)) + events = [event async for event in result.stream_events()] + await asyncio.sleep(0) + + assert events + assert result.final_output == "done" + assert result._sandbox_session is None + assert session.stop_calls == 1 + assert session.shutdown_calls == 1 + assert session.close_dependency_calls == 1 + assert client.delete_calls == 1 + + +@pytest.mark.asyncio +async def test_runner_uses_public_agent_for_non_streaming_output_guardrails() -> None: + seen_agents: list[Agent[None]] = [] + + async def output_guardrail( + _context: RunContextWrapper[None], + guardrail_agent: Agent[None], + _output: object, + ) -> GuardrailFunctionOutput: + seen_agents.append(guardrail_agent) + return GuardrailFunctionOutput(output_info=None, tripwire_triggered=False) + + agent = SandboxAgent( + name="sandbox", + model=FakeModel(initial_output=[get_final_output_message("done")]), + instructions="Base instructions.", + capabilities=[_RecordingCapability(instruction_text="Capability instructions.")], + output_guardrails=[OutputGuardrail(guardrail_function=output_guardrail)], + ) + + result = await Runner.run( + agent, "hello", run_config=_sandbox_run_config(_FakeClient(_FakeSession(Manifest()))) + ) + + assert result.final_output == "done" + assert seen_agents == [agent] + + +@pytest.mark.asyncio +async def test_runner_streamed_immediate_cancel_skips_waiting_for_sandbox_cleanup() -> None: + stop_gate = asyncio.Event() + session = _BlockingStopSession(Manifest(), stop_gate) + client = _FakeClient(session) + agent = SandboxAgent( + name="sandbox", + model=FakeModel(initial_output=[get_final_output_message("done")]), + instructions="Base instructions.", + ) + + result = Runner.run_streamed(agent, "hello", run_config=_sandbox_run_config(client)) + + async def consume_with_cancel() -> None: + async for _event in result.stream_events(): + result.cancel(mode="immediate") + break + + try: + await asyncio.wait_for(consume_with_cancel(), timeout=0.2) + finally: + stop_gate.set() + await asyncio.sleep(0) + + +@pytest.mark.asyncio +async def test_runner_streamed_run_loop_task_waits_for_sandbox_cleanup_and_persisted_state() -> ( + None +): + stop_gate = asyncio.Event() + session = _PersistingStopSession(Manifest(), stop_gate) + client = _FakeClient(session) + model = FakeModel() + model.add_multiple_turn_outputs( + [ + [get_final_output_message("done")], + [get_final_output_message("again")], + ] + ) + agent = SandboxAgent( + name="sandbox", + model=model, + instructions="Base instructions.", + ) + run_config = _sandbox_run_config(client) + + result = Runner.run_streamed(agent, "hello", run_config=run_config) + assert result.run_loop_task is not None + + while session.stop_calls == 0: + await asyncio.sleep(0) + + with pytest.raises(asyncio.TimeoutError): + await asyncio.wait_for(asyncio.shield(result.run_loop_task), timeout=0.05) + + stop_gate.set() + await result.run_loop_task + + state = result.to_state() + assert state._sandbox is not None + session_state = state._sandbox["session_state"] + assert isinstance(session_state, dict) + snapshot = session_state["snapshot"] + assert isinstance(snapshot, dict) + assert snapshot["marker"] == "persisted" + + second = await Runner.run(agent, "again", run_config=run_config) + + assert second.final_output == "again" + + +@pytest.mark.asyncio +async def test_runner_rejects_unix_local_manifest_user_and_group_provisioning() -> None: + workspace_root = Path(tempfile.mkdtemp(prefix="unix-local-users-")) + session = await UnixLocalSandboxClient().create( + manifest=Manifest( + root=str(workspace_root), + users=[User(name="sandbox-user")], + ), + options=None, + ) + + try: + with pytest.raises(ValueError, match="does not support manifest users or groups"): + await session.start() + finally: + shutil.rmtree(workspace_root) + + +@pytest.mark.asyncio +async def test_runner_persists_workspace_and_tool_choice_state_across_sandbox_resume() -> None: + client = UnixLocalSandboxClient() + file_capability = _SessionFileCapability() + + @function_tool(name_override="approval_tool", needs_approval=True) + def approval_tool() -> str: + return "approved" + + model = FakeModel() + model.add_multiple_turn_outputs( + [ + [ + get_function_tool_call( + "write_file", + json.dumps({"path": "note.txt", "content": "persist me"}), + call_id="call_write", + ) + ], + [ + get_function_tool_call( + "approval_tool", + json.dumps({}), + call_id="call_approval", + ) + ], + ] + ) + agent = SandboxAgent( + name="sandbox", + model=model, + instructions="Base instructions.", + tools=[approval_tool], + capabilities=[file_capability], + model_settings=ModelSettings(tool_choice="required"), + ) + + first_run = await Runner.run( + agent, + "hello", + run_config=RunConfig(sandbox=SandboxRunConfig(client=client)), + ) + + assert len(first_run.interruptions) == 1 + state = first_run.to_state() + assert state._sandbox is not None + assert state._sandbox["backend_id"] == "unix_local" + session_state = state._sandbox["session_state"] + assert isinstance(session_state, dict) + snapshot_payload = session_state.get("snapshot") + assert isinstance(snapshot_payload, dict) + assert snapshot_payload.get("type") == "local" + sessions_by_agent = state._sandbox["sessions_by_agent"] + assert isinstance(sessions_by_agent, dict) + assert sessions_by_agent[agent.name] == { + "agent_name": agent.name, + "session_state": session_state, + } + + state_json = state.to_json() + resumed_model = FakeModel() + resumed_model.add_multiple_turn_outputs( + [ + [ + get_function_tool_call( + "read_file", + json.dumps({"path": "note.txt"}), + call_id="call_read", + ) + ], + [get_final_output_message("done")], + ] + ) + resumed_agent = SandboxAgent( + name="sandbox", + model=resumed_model, + instructions="Base instructions.", + tools=[approval_tool], + capabilities=[_SessionFileCapability()], + model_settings=ModelSettings(tool_choice="required"), + ) + + restored_state = await RunState.from_json(resumed_agent, state_json) + restored_state.approve(restored_state.get_interruptions()[0]) + resumed = await Runner.run( + resumed_agent, + restored_state, + run_config=RunConfig(sandbox=SandboxRunConfig(client=client)), + ) + + assert resumed.final_output == "done" + assert resumed_model.last_turn_args["model_settings"].tool_choice is None + assert any( + isinstance(item, ToolCallOutputItem) + and item.output == "persist me" + and item.agent is resumed_agent + for item in resumed.new_items + ) + + +@pytest.mark.asyncio +async def test_runner_restores_all_sandbox_agents_from_run_state_across_handoffs() -> None: + client = UnixLocalSandboxClient() + file_capability = _SessionFileCapability() + + @function_tool(name_override="approval_tool", needs_approval=True) + def approval_tool() -> str: + return "approved" + + triage_model = FakeModel() + worker_model = FakeModel() + worker = SandboxAgent( + name="worker", + model=worker_model, + instructions="Worker instructions.", + tools=[approval_tool], + ) + triage = SandboxAgent( + name="triage", + model=triage_model, + instructions="Triage instructions.", + capabilities=[file_capability], + handoffs=[worker], + ) + worker.handoffs = [triage] + triage_model.add_multiple_turn_outputs( + [ + [ + get_function_tool_call( + "write_file", + json.dumps({"path": "note.txt", "content": "persist triage"}), + call_id="call_write", + ) + ], + [get_handoff_tool_call(worker)], + ] + ) + worker_model.add_multiple_turn_outputs( + [ + [get_function_tool_call("approval_tool", json.dumps({}), call_id="call_approval")], + ] + ) + + first_run = await Runner.run( + triage, + "hello", + run_config=RunConfig(sandbox=SandboxRunConfig(client=client)), + ) + + assert len(first_run.interruptions) == 1 + state = first_run.to_state() + assert state._sandbox is not None + assert state._sandbox["backend_id"] == "unix_local" + assert state._sandbox["current_agent_name"] == worker.name + sessions_by_agent = state._sandbox["sessions_by_agent"] + assert isinstance(sessions_by_agent, dict) + assert set(sessions_by_agent) == {triage.name, worker.name} + + state_json = state.to_json() + resumed_triage_model = FakeModel() + resumed_worker_model = FakeModel() + resumed_worker = SandboxAgent( + name="worker", + model=resumed_worker_model, + instructions="Worker instructions.", + tools=[approval_tool], + ) + resumed_triage = SandboxAgent( + name="triage", + model=resumed_triage_model, + instructions="Triage instructions.", + capabilities=[_SessionFileCapability()], + handoffs=[resumed_worker], + ) + resumed_worker.handoffs = [resumed_triage] + resumed_worker_model.add_multiple_turn_outputs([[get_handoff_tool_call(resumed_triage)]]) + resumed_triage_model.add_multiple_turn_outputs( + [ + [ + get_function_tool_call( + "read_file", + json.dumps({"path": "note.txt"}), + call_id="call_read", + ) + ], + [get_final_output_message("done")], + ] + ) + + restored_state = await RunState.from_json(resumed_triage, state_json) + restored_state.approve(restored_state.get_interruptions()[0]) + resumed = await Runner.run( + resumed_triage, + restored_state, + run_config=RunConfig(sandbox=SandboxRunConfig(client=client)), + ) + + assert resumed.final_output == "done" + assert any( + isinstance(item, ToolCallOutputItem) + and item.output == "persist triage" + and item.agent is resumed_triage + for item in resumed.new_items + ) + + +@pytest.mark.asyncio +async def test_runner_serializes_unique_sandbox_resume_keys_for_duplicate_agent_names() -> None: + client = UnixLocalSandboxClient() + file_capability = _SessionFileCapability() + + @function_tool(name_override="approval_tool", needs_approval=True) + def approval_tool() -> str: + return "approved" + + first_model = FakeModel() + second_model = FakeModel() + first = SandboxAgent( + name="sandbox", + model=first_model, + instructions="First instructions.", + capabilities=[file_capability], + ) + second = SandboxAgent( + name="sandbox", + model=second_model, + instructions="Second instructions.", + tools=[approval_tool], + ) + first.handoffs = [second] + second.handoffs = [first] + first_model.add_multiple_turn_outputs( + [ + [ + get_function_tool_call( + "write_file", + json.dumps({"path": "note.txt", "content": "first"}), + call_id="call_write", + ) + ], + [get_handoff_tool_call(second)], + [ + get_function_tool_call( + "read_file", + json.dumps({"path": "note.txt"}), + call_id="call_read", + ) + ], + [get_final_output_message("done")], + ] + ) + second_model.add_multiple_turn_outputs( + [ + [get_function_tool_call("approval_tool", json.dumps({}), call_id="call_approval")], + [get_handoff_tool_call(first)], + ] + ) + + first_run = await Runner.run( + first, + "hello", + run_config=RunConfig(sandbox=SandboxRunConfig(client=client)), + ) + + state = first_run.to_state() + assert state._sandbox is not None + sessions_by_agent = cast(dict[str, dict[str, object]], state._sandbox["sessions_by_agent"]) + assert len(sessions_by_agent) == 2 + assert state._sandbox["current_agent_key"] in sessions_by_agent + + state.approve(first_run.interruptions[0]) + resumed = await Runner.run( + first, + state, + run_config=RunConfig(sandbox=SandboxRunConfig(client=client)), + ) + + assert resumed.final_output == "done" + assert any( + isinstance(item, ToolCallOutputItem) and item.output == "first" and item.agent is first + for item in resumed.new_items + ) + + +def test_duplicate_name_sandbox_identity_map_uses_capability_and_manifest_config() -> None: + """Duplicate-name sandbox identities should stay stable when only sandbox config differs.""" + + def _make_agent(readme: bytes, capability_text: str) -> SandboxAgent[None]: + return SandboxAgent( + name="sandbox", + model=FakeModel(), + instructions="Base instructions.", + default_manifest=Manifest(entries={"README.md": File(content=readme)}), + capabilities=[_RecordingCapability(instruction_text=capability_text)], + ) + + def _identity_for(identity_map: dict[str, Agent[Any]], target: Agent[Any]) -> str: + return next(identity for identity, agent in identity_map.items() if agent is target) + + first_alpha = _make_agent(b"alpha", "Alpha capability.") + first_beta = _make_agent(b"beta", "Beta capability.") + first_root = Agent(name="triage", handoffs=[first_beta, first_alpha]) + first_alpha.handoffs = [first_root] + first_beta.handoffs = [first_root] + + second_alpha = _make_agent(b"alpha", "Alpha capability.") + second_beta = _make_agent(b"beta", "Beta capability.") + second_root = Agent(name="triage", handoffs=[second_alpha, second_beta]) + second_alpha.handoffs = [second_root] + second_beta.handoffs = [second_root] + + first_identity_map = _build_agent_identity_map(first_root) + second_identity_map = _build_agent_identity_map(second_root) + + assert _identity_for(first_identity_map, first_alpha) == _identity_for( + second_identity_map, second_alpha + ) + assert _identity_for(first_identity_map, first_beta) == _identity_for( + second_identity_map, second_beta + ) + + +@pytest.mark.asyncio +async def test_session_manager_reserves_current_duplicate_resume_key_for_current_agent() -> None: + manifest = Manifest(entries={"README.md": File(content=b"duplicate resume")}) + client = _FakeClient(_FakeSession(manifest)) + first = SandboxAgent(name="sandbox", model=FakeModel(), instructions="First.") + second = SandboxAgent(name="sandbox", model=FakeModel(), instructions="Second.") + first.handoffs = [second] + second.handoffs = [first] + first_session_state = client.serialize_session_state( + SandboxSessionState(manifest=Manifest(), snapshot=NoopSnapshot(id="first")) + ) + second_session_state = client.serialize_session_state( + SandboxSessionState(manifest=Manifest(), snapshot=NoopSnapshot(id="second")) + ) + run_state: RunState[Any, Agent[Any]] = cast( + RunState[Any, Agent[Any]], + RunState( + context=RunContextWrapper(context={}), + original_input="hello", + starting_agent=first, + ), + ) + run_state._current_agent = second + run_state._sandbox = { + "backend_id": "fake", + "current_agent_key": "sandbox#2", + "current_agent_name": second.name, + "session_state": second_session_state, + "sessions_by_agent": { + "sandbox": {"agent_name": first.name, "session_state": first_session_state}, + "sandbox#2": {"agent_name": second.name, "session_state": second_session_state}, + }, + } + manager = SandboxRuntimeSessionManager( + starting_agent=first, + sandbox_config=SandboxRunConfig(client=client, options={"image": "sandbox"}), + run_state=run_state, + ) + + assert ( + manager._resume_state_payload_for_agent(client=client, agent=first, agent_id=id(first)) + == first_session_state + ) + assert ( + manager._resume_state_payload_for_agent(client=client, agent=second, agent_id=id(second)) + == second_session_state + ) + + +def test_session_manager_generates_collision_free_resume_keys_for_literal_suffix_names() -> None: + client = _FakeClient(_FakeSession(Manifest())) + first = SandboxAgent(name="sandbox", model=FakeModel(), instructions="First.") + literal_suffix = SandboxAgent(name="sandbox#2", model=FakeModel(), instructions="Literal.") + second = SandboxAgent(name="sandbox", model=FakeModel(), instructions="Second.") + first.handoffs = [literal_suffix, second] + literal_suffix.handoffs = [first, second] + second.handoffs = [first, literal_suffix] + manager = SandboxRuntimeSessionManager( + starting_agent=first, + sandbox_config=SandboxRunConfig(client=client, options={"image": "sandbox"}), + run_state=None, + ) + + manager.acquire_agent(first) + manager.acquire_agent(literal_suffix) + manager.acquire_agent(second) + + assert manager._ensure_resume_key(first) == "sandbox" + assert manager._ensure_resume_key(literal_suffix) == "sandbox#2" + assert manager._ensure_resume_key(second) == "sandbox#3" + + +@pytest.mark.asyncio +async def test_session_manager_preserves_untouched_run_state_sessions_on_cleanup() -> None: + manifest = Manifest(entries={"README.md": File(content=b"duplicate resume")}) + client = _FakeClient(_FakeSession(manifest)) + triage = SandboxAgent(name="triage", model=FakeModel(), instructions="Triage.") + worker = SandboxAgent(name="worker", model=FakeModel(), instructions="Worker.") + triage.handoffs = [worker] + worker.handoffs = [triage] + triage_session_state = client.serialize_session_state( + SandboxSessionState(manifest=Manifest(), snapshot=NoopSnapshot(id="triage")) + ) + worker_session_state = client.serialize_session_state( + SandboxSessionState(manifest=Manifest(), snapshot=NoopSnapshot(id="worker")) + ) + run_state: RunState[Any, Agent[Any]] = cast( + RunState[Any, Agent[Any]], + RunState( + context=RunContextWrapper(context={}), + original_input="hello", + starting_agent=triage, + ), + ) + run_state._current_agent = worker + run_state._sandbox = { + "backend_id": "fake", + "current_agent_key": worker.name, + "current_agent_name": worker.name, + "session_state": worker_session_state, + "sessions_by_agent": { + triage.name: {"agent_name": triage.name, "session_state": triage_session_state}, + worker.name: {"agent_name": worker.name, "session_state": worker_session_state}, + }, + } + manager = SandboxRuntimeSessionManager( + starting_agent=triage, + sandbox_config=SandboxRunConfig(client=client, options={"image": "sandbox"}), + run_state=run_state, + ) + + manager.acquire_agent(worker) + await manager.ensure_session(agent=worker, capabilities=[], is_resumed_state=True) + payload = await manager.cleanup() + + assert payload is not None + sessions_by_agent = cast(dict[str, dict[str, object]], payload["sessions_by_agent"]) + assert set(sessions_by_agent) == {triage.name, worker.name} + assert sessions_by_agent[triage.name] == { + "agent_name": triage.name, + "session_state": triage_session_state, + } + assert sessions_by_agent[worker.name] == { + "agent_name": worker.name, + "session_state": worker_session_state, + } + + +@pytest.mark.asyncio +@pytest.mark.parametrize("resume_source", ["run_state", "session_state"]) +async def test_session_manager_reapplies_capability_manifest_mutations_on_resume( + resume_source: str, +) -> None: + client = _FakeClient(_FakeSession(Manifest())) + capability = _ManifestMutationCapability() + agent = SandboxAgent(name="worker", model=FakeModel(), instructions="Worker.") + session_state = SandboxSessionState( + manifest=Manifest(), + snapshot=NoopSnapshot(id="resume"), + ) + + run_state: RunState[Any, Agent[Any]] | None = None + if resume_source == "run_state": + run_state = cast( + RunState[Any, Agent[Any]], + RunState( + context=RunContextWrapper(context={}), + original_input="hello", + starting_agent=agent, + ), + ) + run_state._current_agent = agent + serialized_state = client.serialize_session_state(session_state) + run_state._sandbox = { + "backend_id": client.backend_id, + "current_agent_key": agent.name, + "current_agent_name": agent.name, + "session_state": serialized_state, + "sessions_by_agent": { + agent.name: { + "agent_name": agent.name, + "session_state": serialized_state, + } + }, + } + sandbox_config = SandboxRunConfig(client=client, options={"image": "sandbox"}) + else: + sandbox_config = SandboxRunConfig( + client=client, + session_state=session_state, + options={"image": "sandbox"}, + ) + + manager = SandboxRuntimeSessionManager( + starting_agent=agent, + sandbox_config=sandbox_config, + run_state=run_state, + ) + + manager.acquire_agent(agent) + session = await manager.ensure_session( + agent=agent, + capabilities=[capability], + is_resumed_state=True, + ) + + assert session.state.manifest.entries["cap.txt"] == File(content=b"capability") + assert client.resume_state is not None + assert client.resume_state.manifest.entries["cap.txt"] == File(content=b"capability") + + +@pytest.mark.asyncio +async def test_session_manager_preserves_existing_payload_when_no_sandbox_session_is_used() -> None: + client = _FakeClient(_FakeSession(Manifest())) + agent = SandboxAgent(name="sandbox", model=FakeModel(), instructions="Base instructions.") + run_state: RunState[Any, Agent[Any]] = cast( + RunState[Any, Agent[Any]], + RunState( + context=RunContextWrapper(context={}), + original_input="hello", + starting_agent=agent, + ), + ) + existing_payload = { + "backend_id": "fake", + "current_agent_key": agent.name, + "current_agent_name": agent.name, + "session_state": {"snapshot": {"id": "persisted"}}, + "sessions_by_agent": { + agent.name: { + "agent_name": agent.name, + "session_state": {"snapshot": {"id": "persisted"}}, + } + }, + } + run_state._sandbox = existing_payload + manager = SandboxRuntimeSessionManager( + starting_agent=agent, + sandbox_config=SandboxRunConfig(client=client, options={"image": "sandbox"}), + run_state=run_state, + ) + + payload = await manager.cleanup() + + assert payload == existing_payload + assert payload is not existing_payload + + +@pytest.mark.asyncio +async def test_session_manager_uses_run_state_starting_agent_for_duplicate_resume_keys() -> None: + manifest = Manifest(entries={"README.md": File(content=b"duplicate resume")}) + client = _FakeClient(_FakeSession(manifest)) + first = SandboxAgent(name="sandbox", model=FakeModel(), instructions="First.") + second = SandboxAgent(name="sandbox", model=FakeModel(), instructions="Second.") + approver = Agent(name="approver", model=FakeModel(), instructions="Approve.", handoffs=[]) + approver.handoffs = [second, first] + first.handoffs = [second] + second.handoffs = [approver] + first_session_state = client.serialize_session_state( + SandboxSessionState(manifest=Manifest(), snapshot=NoopSnapshot(id="first")) + ) + second_session_state = client.serialize_session_state( + SandboxSessionState(manifest=Manifest(), snapshot=NoopSnapshot(id="second")) + ) + run_state: RunState[Any, Agent[Any]] = cast( + RunState[Any, Agent[Any]], + RunState( + context=RunContextWrapper(context={}), + original_input="hello", + starting_agent=first, + ), + ) + run_state._current_agent = approver + run_state._starting_agent = first + run_state._sandbox = { + "backend_id": "fake", + "current_agent_key": "sandbox#2", + "current_agent_name": second.name, + "session_state": second_session_state, + "sessions_by_agent": { + "sandbox": {"agent_name": first.name, "session_state": first_session_state}, + "sandbox#2": {"agent_name": second.name, "session_state": second_session_state}, + }, + } + manager = SandboxRuntimeSessionManager( + starting_agent=approver, + sandbox_config=SandboxRunConfig(client=client, options={"image": "sandbox"}), + run_state=run_state, + ) + + assert ( + manager._resume_state_payload_for_agent(client=client, agent=first, agent_id=id(first)) + == first_session_state + ) + assert ( + manager._resume_state_payload_for_agent(client=client, agent=second, agent_id=id(second)) + == second_session_state + ) + + +@pytest.mark.asyncio +async def test_session_manager_restores_duplicate_name_sessions_when_only_sandbox_config_differs(): + client = _FakeClient(_FakeSession(Manifest())) + + def _make_agent(readme: bytes, capability_text: str) -> SandboxAgent[None]: + return SandboxAgent( + name="sandbox", + model=FakeModel(), + instructions="Base instructions.", + default_manifest=Manifest(entries={"README.md": File(content=readme)}), + capabilities=[_RecordingCapability(instruction_text=capability_text)], + ) + + first = _make_agent(b"first", "First capability.") + second = _make_agent(b"second", "Second capability.") + root = Agent(name="triage", handoffs=[second, first]) + first.handoffs = [root] + second.handoffs = [root] + + first_session_state = client.serialize_session_state( + SandboxSessionState(manifest=Manifest(), snapshot=NoopSnapshot(id="first")) + ) + second_session_state = client.serialize_session_state( + SandboxSessionState(manifest=Manifest(), snapshot=NoopSnapshot(id="second")) + ) + + state: RunState[Any, Agent[Any]] = cast( + RunState[Any, Agent[Any]], + RunState( + context=RunContextWrapper(context={}), + original_input="hello", + starting_agent=root, + ), + ) + state._current_agent = second + state._sandbox = { + "backend_id": "fake", + "current_agent_key": "sandbox#2", + "current_agent_name": second.name, + "session_state": second_session_state, + "sessions_by_agent": { + "sandbox": {"agent_name": first.name, "session_state": first_session_state}, + "sandbox#2": {"agent_name": second.name, "session_state": second_session_state}, + }, + } + + restored_first = _make_agent(b"first", "First capability.") + restored_second = _make_agent(b"second", "Second capability.") + restored_root = Agent(name="triage", handoffs=[restored_first, restored_second]) + restored_first.handoffs = [restored_root] + restored_second.handoffs = [restored_root] + + restored_state = await RunState.from_json(restored_root, state.to_json()) + assert restored_state._current_agent is restored_second + + manager = SandboxRuntimeSessionManager( + starting_agent=restored_root, + sandbox_config=SandboxRunConfig(client=client, options={"image": "sandbox"}), + run_state=restored_state, + ) + + assert ( + manager._resume_state_payload_for_agent( + client=client, + agent=restored_first, + agent_id=id(restored_first), + ) + == first_session_state + ) + assert ( + manager._resume_state_payload_for_agent( + client=client, + agent=restored_second, + agent_id=id(restored_second), + ) + == second_session_state + ) + + +@pytest.mark.asyncio +async def test_runner_restores_duplicate_name_sandbox_sessions_after_json_roundtrip() -> None: + client = UnixLocalSandboxClient() + file_capability = _SessionFileCapability() + + @function_tool(name_override="approval_tool", needs_approval=True) + def approval_tool() -> str: + return "approved" + + first_model = FakeModel() + second_model = FakeModel() + first = SandboxAgent( + name="sandbox", + model=first_model, + instructions="First instructions.", + capabilities=[file_capability], + ) + second = SandboxAgent( + name="sandbox", + model=second_model, + instructions="Second instructions.", + tools=[approval_tool], + ) + first.handoffs = [second] + second.handoffs = [first] + first_model.add_multiple_turn_outputs( + [ + [ + get_function_tool_call( + "write_file", + json.dumps({"path": "note.txt", "content": "first"}), + call_id="call_write", + ) + ], + [get_handoff_tool_call(second)], + ] + ) + second_model.add_multiple_turn_outputs( + [[get_function_tool_call("approval_tool", json.dumps({}), call_id="call_approval")]] + ) + + first_run = await Runner.run( + first, + "hello", + run_config=RunConfig(sandbox=SandboxRunConfig(client=client)), + ) + + state = first_run.to_state() + state_json = state.to_json() + + resumed_first_model = FakeModel() + resumed_second_model = FakeModel() + resumed_first = SandboxAgent( + name="sandbox", + model=resumed_first_model, + instructions="First instructions.", + capabilities=[_SessionFileCapability()], + ) + resumed_second = SandboxAgent( + name="sandbox", + model=resumed_second_model, + instructions="Second instructions.", + tools=[approval_tool], + ) + resumed_first.handoffs = [resumed_second] + resumed_second.handoffs = [resumed_first] + resumed_second_model.add_multiple_turn_outputs([[get_handoff_tool_call(resumed_first)]]) + resumed_first_model.add_multiple_turn_outputs( + [ + [ + get_function_tool_call( + "read_file", + json.dumps({"path": "note.txt"}), + call_id="call_read", + ) + ], + [get_final_output_message("done")], + ] + ) + + restored_state = await RunState.from_json(resumed_first, state_json) + restored_state.approve(restored_state.get_interruptions()[0]) + resumed = await Runner.run( + resumed_first, + restored_state, + run_config=RunConfig(sandbox=SandboxRunConfig(client=client)), + ) + + assert resumed.final_output == "done" + assert any( + isinstance(item, ToolCallOutputItem) + and item.output == "first" + and item.agent is resumed_first + for item in resumed.new_items + ) + + +@pytest.mark.asyncio +async def test_runner_restores_legacy_current_sandbox_payload_after_json_roundtrip() -> None: + client = UnixLocalSandboxClient() + + @function_tool(name_override="approval_tool", needs_approval=True) + def approval_tool() -> str: + return "approved" + + initial_model = FakeModel() + initial_model.add_multiple_turn_outputs( + [ + [ + get_function_tool_call( + "write_file", json.dumps({"path": "note.txt", "content": "legacy"}) + ) + ], + [get_function_tool_call("approval_tool", json.dumps({}), call_id="call_approval")], + ] + ) + agent = SandboxAgent( + name="sandbox", + model=initial_model, + instructions="Base instructions.", + tools=[approval_tool], + capabilities=[_SessionFileCapability()], + ) + + first_run = await Runner.run( + agent, + "hello", + run_config=RunConfig(sandbox=SandboxRunConfig(client=client)), + ) + state = first_run.to_state() + assert state._sandbox is not None + session_state = cast(dict[str, object], state._sandbox["session_state"]) + state._sandbox = { + "backend_id": "unix_local", + "current_agent_id": id(agent), + "session_state": session_state, + "sessions_by_agent": {str(id(agent)): session_state}, + } + + resumed_model = FakeModel() + resumed_model.add_multiple_turn_outputs( + [ + [ + get_function_tool_call( + "read_file", json.dumps({"path": "note.txt"}), call_id="call_read" + ) + ], + [get_final_output_message("done")], + ] + ) + resumed_agent = SandboxAgent( + name="sandbox", + model=resumed_model, + instructions="Base instructions.", + tools=[approval_tool], + capabilities=[_SessionFileCapability()], + ) + + restored_state = await RunState.from_json(resumed_agent, state.to_json()) + restored_state.approve(restored_state.get_interruptions()[0]) + resumed = await Runner.run( + resumed_agent, + restored_state, + run_config=RunConfig(sandbox=SandboxRunConfig(client=client)), + ) + + assert resumed.final_output == "done" + assert any( + isinstance(item, ToolCallOutputItem) + and item.output == "legacy" + and item.agent is resumed_agent + for item in resumed.new_items + ) + + +@pytest.mark.asyncio +@pytest.mark.skipif( + sys.platform != "darwin" or shutil.which("sandbox-exec") is None, + reason="sandbox-exec is only available on macOS when installed", +) +async def test_unix_local_exec_confines_commands_to_workspace_root() -> None: + workspace_root = Path(tempfile.mkdtemp(prefix="unix-local-exec-")) + session = await UnixLocalSandboxClient().create( + manifest=Manifest(root=str(workspace_root)), + options=None, + ) + + try: + async with session: + result = await session.exec("echo hi > note.txt && cat note.txt") + assert result.ok() + assert result.stdout.decode("utf-8", errors="replace").strip().endswith("hi") + + forbidden = await session.exec("cat /etc/passwd >/dev/null") + assert not forbidden.ok() + + outside_write = await session.exec("echo nope > /usr/local/test-codex-sandbox") + assert not outside_write.ok() + + sibling = workspace_root.parent / "escape.txt" + sibling.unlink(missing_ok=True) + escaped = await session.exec("echo nope > ../escape.txt") + assert not escaped.ok() + assert not sibling.exists() + finally: + shutil.rmtree(workspace_root, ignore_errors=True) + + +@pytest.mark.asyncio +async def test_unix_local_exec_rejects_when_confinement_is_unavailable( + monkeypatch: pytest.MonkeyPatch, +) -> None: + workspace_root = Path(tempfile.mkdtemp(prefix="unix-local-exec-")) + session = await UnixLocalSandboxClient().create( + manifest=Manifest(root=str(workspace_root)), + options=None, + ) + unix_local = cast(Any, unix_local_module) + monkeypatch.setattr(unix_local.sys, "platform", "darwin") + monkeypatch.setattr(unix_local.shutil, "which", lambda _name: None) + + try: + with pytest.raises(ExecTransportError) as exc_info: + await session.exec("pwd") + finally: + shutil.rmtree(workspace_root, ignore_errors=True) + + assert exc_info.value.context["reason"] == "unix_local_confinement_unavailable" + + +@pytest.mark.asyncio +async def test_unix_local_exec_runs_without_wrapper_on_linux( + monkeypatch: pytest.MonkeyPatch, +) -> None: + workspace_root = Path(tempfile.mkdtemp(prefix="unix-local-exec-")) + session = await UnixLocalSandboxClient().create( + manifest=Manifest(root=str(workspace_root)), + options=None, + ) + unix_local = cast(Any, unix_local_module) + monkeypatch.setattr(unix_local.sys, "platform", "linux") + + try: + async with session: + result = await session.exec("pwd") + finally: + shutil.rmtree(workspace_root, ignore_errors=True) + + assert result.ok() + assert result.stdout.decode("utf-8", errors="replace").strip() == str(workspace_root.resolve()) + + +def test_unix_local_confined_exec_command_allows_common_darwin_interpreter_roots( + monkeypatch: pytest.MonkeyPatch, +) -> None: + workspace_root = Path(tempfile.mkdtemp(prefix="unix-local-exec-")) + session = UnixLocalSandboxSession.from_state( + UnixLocalSandboxSessionState( + session_id=uuid.uuid4(), + manifest=Manifest(root=str(workspace_root)), + snapshot=NoopSnapshot(id="darwin"), + workspace_root_owned=False, + ) + ) + unix_local = cast(Any, unix_local_module) + host_home = Path.home() + path_env = os.pathsep.join( + [ + "/opt/homebrew/bin", + "/usr/local/bin", + str(host_home / ".local" / "bin"), + ] + ) + + def _fake_which(name: str, path: str | None = None) -> str | None: + if name == "sandbox-exec": + return "/usr/bin/sandbox-exec" + if name == "python3": + assert path == path_env + return "/opt/homebrew/bin/python3" + return None + + monkeypatch.setattr(unix_local.sys, "platform", "darwin") + monkeypatch.setattr(unix_local.shutil, "which", _fake_which) + + command = session._confined_exec_command( + command_parts=["python3", "-V"], + workspace_root=workspace_root, + env={"PATH": path_env}, + ) + profile = command[2] + + assert command[:2] == ["/usr/bin/sandbox-exec", "-p"] + assert '(allow file-read-data file-read-metadata (subpath "/opt/homebrew"))' in profile + assert '(allow file-read-data file-read-metadata (subpath "/usr/local"))' in profile + assert ( + f'(allow file-read-data file-read-metadata (subpath "{host_home / ".local"}"))' in profile + ) + assert '(deny file-write* (subpath "/opt"))' in profile + assert '(allow file-write* (subpath "/opt/homebrew"))' not in profile + + +@pytest.mark.asyncio +async def test_sandbox_run_persists_only_new_session_input_items() -> None: + session = SimpleListSession( + history=[ + { + "role": "user", + "content": "old", + } + ] + ) + model = FakeModel(initial_output=[get_final_output_message("done")]) + agent = SandboxAgent( + name="sandbox", + model=model, + instructions="Base instructions.", + ) + + result = await Runner.run( + agent, + "new", + session=session, + run_config=_sandbox_run_config(_FakeClient(_FakeSession(Manifest()))), + ) + + assert result.final_output == "done" + saved_user_items = [ + item + for item in await session.get_items() + if isinstance(item, dict) and item.get("role") == "user" + ] + assert saved_user_items == [ + {"role": "user", "content": "old"}, + {"role": "user", "content": "new"}, + ] + + +@pytest.mark.asyncio +async def test_runner_streamed_emits_public_agent_for_tool_and_reasoning_events() -> None: + model = FakeModel() + model.add_multiple_turn_outputs( + [ + [ + _get_reasoning_item(), + get_function_tool_call("tool1", json.dumps({}), call_id="call_tool"), + ], + [get_final_output_message("done")], + ] + ) + agent = SandboxAgent( + name="sandbox", + model=model, + instructions="Base instructions.", + tools=[get_function_tool("tool1", "tool result")], + ) + + result = Runner.run_streamed( + agent, + "hello", + run_config=_sandbox_run_config(_FakeClient(_FakeSession(Manifest()))), + ) + events = [event async for event in result.stream_events()] + relevant_events = [ + event + for event in events + if isinstance(event, RunItemStreamEvent) + and event.name in {"reasoning_item_created", "tool_called", "tool_output"} + ] + + assert relevant_events + assert all(event.item.agent is agent for event in relevant_events) + + +def test_capability_clone_deep_copies_nested_mutable_state() -> None: + capability = _NestedStateCapability() + + cloned = cast(_NestedStateCapability, capability.clone()) + cloned.state["seen"].append("turn-1") + + assert capability.state == {"seen": []} + assert cloned.state == {"seen": ["turn-1"]} + + +def test_capability_clone_deep_copies_nested_object_state() -> None: + capability = _NestedObjectCapability() + + cloned = cast(_NestedObjectCapability, capability.clone()) + cloned.state.seen.append("turn-1") + + assert capability.state.seen == [] + assert cloned.state.seen == ["turn-1"] + + +@pytest.mark.asyncio +async def test_apply_manifest_raises_on_account_provisioning_failures() -> None: + session = _ProvisioningFailureSession( + Manifest(users=[User(name="sandbox-user")]), + ) + + with pytest.raises(ExecNonZeroError): + await session.apply_manifest() + + +@pytest.mark.asyncio +async def test_apply_manifest_only_ephemeral_skips_account_provisioning_failures() -> None: + session = _ProvisioningFailureSession( + Manifest(users=[User(name="sandbox-user")]), + ) + + result = await session.apply_manifest(only_ephemeral=True) + + assert result.files == [] + + +@pytest.mark.asyncio +async def test_resume_reprovisions_manifest_accounts_before_reapplying_ephemeral_entries() -> None: + session = _RestorableProvisioningFailureSession( + Manifest(users=[User(name="sandbox-user")]), + ) + + with pytest.raises(ExecNonZeroError): + await session.start() + + assert session.cleared_workspace_root is True + assert session.hydrate_calls == 1 + + +@pytest.mark.asyncio +async def test_resume_can_skip_manifest_account_reprovisioning_when_os_state_is_preserved() -> None: + session = _RestorableProvisioningFailureSession( + Manifest(users=[User(name="sandbox-user")]), + provision_on_resume=False, + ) + + await session.start() + + assert session.cleared_workspace_root is True + assert session.hydrate_calls == 1 + + +@pytest.mark.asyncio +async def test_prepare_agent_rechecks_session_liveness_before_reusing_cached_agent() -> None: + session = _FakeSession(Manifest()) + client = _FakeClient(session) + agent = SandboxAgent( + name="sandbox", + model=FakeModel(initial_output=[get_final_output_message("done")]), + instructions="Base instructions.", + ) + runtime = SandboxRuntime( + starting_agent=agent, + run_config=_sandbox_run_config(client), + run_state=None, + ) + context_wrapper = RunContextWrapper(context=None) + + first_prepared = await runtime.prepare_agent( + current_agent=agent, + current_input="hello", + context_wrapper=context_wrapper, + is_resumed_state=False, + ) + assert session.start_calls == 1 + + session._running = False + + second_prepared = await runtime.prepare_agent( + current_agent=agent, + current_input="hello again", + context_wrapper=context_wrapper, + is_resumed_state=False, + ) + + assert second_prepared.bindings.execution_agent is first_prepared.bindings.execution_agent + assert session.start_calls == 2 + + +@pytest.mark.asyncio +async def test_prepare_agent_starts_new_live_session_even_when_backend_reports_running() -> None: + session = _FakeSession(Manifest()) + session._running = True + client = _FakeClient(session) + agent = SandboxAgent( + name="sandbox", + model=FakeModel(initial_output=[get_final_output_message("done")]), + instructions="Base instructions.", + ) + runtime = SandboxRuntime( + starting_agent=agent, + run_config=_sandbox_run_config(client), + run_state=None, + ) + + await runtime.prepare_agent( + current_agent=agent, + current_input="hello", + context_wrapper=RunContextWrapper(context=None), + is_resumed_state=False, + ) + + assert session.start_calls == 1 + + +@pytest.mark.asyncio +async def test_runner_uses_public_agent_for_non_function_tool_outputs() -> None: + tool = LocalShellTool(executor=lambda _request: "shell result") + action = LocalShellCallAction( + command=["bash", "-lc", "echo sandbox"], + env={}, + type="exec", + timeout_ms=1000, + working_directory="/workspace", + ) + local_shell_call = LocalShellCall( + id="lsh_sandbox", + action=action, + call_id="call_local_shell", + status="completed", + type="local_shell_call", + ) + + model = FakeModel() + model.add_multiple_turn_outputs( + [ + [local_shell_call], + [get_final_output_message("done")], + ] + ) + + agent = SandboxAgent( + name="sandbox", + model=model, + instructions="Base instructions.", + tools=[tool], + ) + + result = await Runner.run( + agent, + "hello", + run_config=_sandbox_run_config(_FakeClient(_FakeSession(Manifest()))), + ) + + output_items = [ + item + for item in result.new_items + if isinstance(item, ToolCallOutputItem) + and isinstance(item.raw_item, dict) + and item.raw_item.get("type") == "local_shell_call_output" + ] + + assert output_items + assert all(item.agent is agent for item in output_items) + + +@pytest.mark.asyncio +async def test_sandbox_agent_as_tool_uses_runner_sandbox_prep() -> None: + child_model = FakeModel(initial_output=[get_final_output_message("child done")]) + parent_model = FakeModel( + initial_output=[ + get_function_tool_call("delegate_to_child", json.dumps({"input": "check sandbox"})) + ] + ) + parent_model.set_next_output([get_final_output_message("parent done")]) + + capability = _RecordingCapability(instruction_text="Use the sandbox carefully.") + manifest = Manifest(entries={"README.md": File(content=b"Use repo-safe commands only.")}) + session = _FakeSession(manifest) + client = _FakeClient(session) + + child = SandboxAgent( + name="child", + model=child_model, + instructions="Child base instructions.", + default_manifest=manifest, + capabilities=[capability], + ) + parent = Agent( + name="parent", + model=parent_model, + instructions="Parent instructions.", + tools=[child.as_tool("delegate_to_child", "Delegate to the sandbox child.")], + ) + + result = await Runner.run( + parent, + "hello", + run_config=_sandbox_run_config(client), + ) + + assert result.final_output == "parent done" + assert capability.bound_session is None + assert child_model.first_turn_args is not None + child_input = child_model.first_turn_args["input"] + assert isinstance(child_input, list) + assert _extract_user_text(child_input[0]) == "check sandbox" + + +@pytest.mark.asyncio +async def test_runner_reapplies_sandbox_prep_on_handoff() -> None: + triage_model = FakeModel() + worker_model = FakeModel(initial_output=[get_final_output_message("done")]) + manifest = Manifest(entries={"README.md": File(content=b"Shared repo instructions.")}) + session = _FakeSession(manifest) + client = _FakeClient(session) + + capability_one = _RecordingCapability(instruction_text="Triage capability.") + capability_two = _RecordingCapability(instruction_text="Worker capability.") + worker = SandboxAgent( + name="worker", + model=worker_model, + instructions="Worker instructions.", + default_manifest=manifest, + capabilities=[capability_two], + ) + triage = SandboxAgent( + name="triage", + model=triage_model, + instructions="Triage instructions.", + default_manifest=manifest, + capabilities=[capability_one], + handoffs=[worker], + ) + triage_model.turn_outputs = [[get_handoff_tool_call(worker)]] + + result = await Runner.run( + triage, + "route this", + run_config=_sandbox_run_config(client), + ) + + assert result.final_output == "done" + assert capability_one.bound_session is None + assert capability_two.bound_session is None + assert worker_model.first_turn_args is not None + assert worker_model.first_turn_args["system_instructions"] == ( + "Worker instructions.\n\nWorker capability." + ) + + +@pytest.mark.asyncio +async def test_runner_restores_sandbox_from_run_state() -> None: + model = FakeModel() + + @function_tool(name_override="approval_tool", needs_approval=True) + def approval_tool() -> str: + return "approved" + + manifest = Manifest(entries={"README.md": File(content=b"Resume with sandbox state.")}) + session = _FakeSession(manifest) + client = _FakeClient(session) + agent = SandboxAgent( + name="sandbox", + model=model, + instructions="Base instructions.", + tools=[approval_tool], + default_manifest=manifest, + ) + model.add_multiple_turn_outputs( + [ + [get_function_tool_call("approval_tool", json.dumps({}), call_id="call_resume")], + [get_final_output_message("done")], + ] + ) + + first_run = await Runner.run( + agent, + "hello", + run_config=_sandbox_run_config(client), + ) + + assert len(first_run.interruptions) == 1 + state = first_run.to_state() + assert state._sandbox is not None + state.approve(first_run.interruptions[0]) + + resumed = await Runner.run( + agent, + state, + run_config=_sandbox_run_config(client), + ) + + assert resumed.final_output == "done" + assert client.resume_state is not None + + +@pytest.mark.asyncio +async def test_runner_rejects_concurrent_reuse_of_same_sandbox_agent() -> None: + model = FakeModel(initial_output=[get_final_output_message("done")]) + start_gate = asyncio.Event() + session = _FakeSession(Manifest(), start_gate=start_gate) + client = _FakeClient(session) + agent = SandboxAgent( + name="sandbox", + model=model, + instructions="Base instructions.", + ) + run_config = _sandbox_run_config(client) + + first_run = asyncio.create_task(Runner.run(agent, "hello", run_config=run_config)) + while session.start_calls == 0: + await asyncio.sleep(0) + + with pytest.raises(RuntimeError, match="cannot be reused concurrently"): + await Runner.run(agent, "again", run_config=run_config) + + start_gate.set() + result = await first_run + assert result.final_output == "done" + + +@pytest.mark.asyncio +async def test_runner_isolates_shared_capabilities_per_run() -> None: + release_gate = asyncio.Event() + first_instruction_started = asyncio.Event() + second_instruction_started = asyncio.Event() + shared_capability = _AwaitableSessionCapability( + release_gate=release_gate, + first_instruction_started=first_instruction_started, + second_instruction_started=second_instruction_started, + ) + + session_one = _FakeSession( + Manifest(entries={"README.md": File(content=b"Session one instructions.")}) + ) + session_two = _FakeSession( + Manifest(entries={"README.md": File(content=b"Session two instructions.")}) + ) + client_one = _FakeClient(session_one) + client_two = _FakeClient(session_two) + model_one = FakeModel(initial_output=[get_final_output_message("done one")]) + model_two = FakeModel(initial_output=[get_final_output_message("done two")]) + agent_one = SandboxAgent( + name="sandbox-one", + model=model_one, + instructions="Base instructions.", + capabilities=[shared_capability], + ) + agent_two = SandboxAgent( + name="sandbox-two", + model=model_two, + instructions="Base instructions.", + capabilities=[shared_capability], + ) + + first_run = asyncio.create_task( + Runner.run(agent_one, "hello one", run_config=_sandbox_run_config(client_one)) + ) + await first_instruction_started.wait() + + second_run = asyncio.create_task( + Runner.run(agent_two, "hello two", run_config=_sandbox_run_config(client_two)) + ) + await second_instruction_started.wait() + + release_gate.set() + first_result, second_result = await asyncio.gather(first_run, second_run) + + assert first_result.final_output == "done one" + assert second_result.final_output == "done two" + assert model_one.first_turn_args is not None + assert model_two.first_turn_args is not None + assert ( + model_one.first_turn_args["system_instructions"] + == "Base instructions.\n\nSession one instructions." + ) + assert ( + model_two.first_turn_args["system_instructions"] + == "Base instructions.\n\nSession two instructions." + ) + assert shared_capability.bound_session is None + + +@pytest.mark.asyncio +async def test_runner_deep_clones_capability_runtime_state() -> None: + model = FakeModel(initial_output=[get_final_output_message("done")]) + session = _FakeSession(Manifest(entries={"README.md": File(content=b"hello")})) + client = _FakeClient(session) + + class _MutableCapability(Capability): + def __init__(self) -> None: + super().__init__(type="mutable") + self.bound_labels: list[str] = [] + + def bind(self, session: BaseSandboxSession) -> None: + readme = session.state.manifest.entries["README.md"] + assert isinstance(readme, File) + self.bound_labels.append(readme.content.decode()) + + capability = _MutableCapability() + agent = SandboxAgent( + name="sandbox", + model=model, + instructions="Base instructions.", + capabilities=[capability], + ) + + result = await Runner.run(agent, "hello", run_config=_sandbox_run_config(client)) + + assert result.final_output == "done" + assert capability.bound_labels == [] + + +@pytest.mark.asyncio +async def test_runner_keeps_public_agent_identity_for_hooks_and_streaming() -> None: + model = FakeModel(initial_output=[get_final_output_message("done")]) + session = _FakeSession(Manifest()) + client = _FakeClient(session) + run_hooks = _RecordingRunHooks() + agent_hooks = _RecordingAgentHooks() + agent = SandboxAgent( + name="sandbox", + model=model, + instructions="Base instructions.", + hooks=agent_hooks, + capabilities=[_RecordingCapability(instruction_text="Capability instructions.")], + ) + + result = await Runner.run( + agent, + "hello", + run_config=_sandbox_run_config(client), + hooks=run_hooks, + ) + + assert result.last_agent is agent + assert run_hooks.started_agents == [agent] + assert run_hooks.ended_agents == [agent] + assert run_hooks.llm_started_agents == [agent] + assert run_hooks.llm_ended_agents == [agent] + assert agent_hooks.started_agents == [agent] + assert agent_hooks.ended_agents == [agent] + assert agent_hooks.llm_started_agents == [agent] + assert agent_hooks.llm_ended_agents == [agent] + assert all(item.agent is agent for item in result.new_items) + + streamed_model = FakeModel(initial_output=[get_final_output_message("streamed done")]) + streamed_session = _FakeSession(Manifest()) + streamed_client = _FakeClient(streamed_session) + streamed_run_hooks = _RecordingRunHooks() + streamed_agent_hooks = _RecordingAgentHooks() + streamed_agent = SandboxAgent( + name="streamed-sandbox", + model=streamed_model, + instructions="Base instructions.", + hooks=streamed_agent_hooks, + capabilities=[_RecordingCapability(instruction_text="Capability instructions.")], + ) + + streamed_result = Runner.run_streamed( + streamed_agent, + "hello", + run_config=_sandbox_run_config(streamed_client), + hooks=streamed_run_hooks, + ) + streamed_events = [event async for event in streamed_result.stream_events()] + run_item_events = [event for event in streamed_events if isinstance(event, RunItemStreamEvent)] + + assert streamed_result.current_agent is streamed_agent + assert streamed_run_hooks.started_agents == [streamed_agent] + assert streamed_run_hooks.ended_agents == [streamed_agent] + assert streamed_run_hooks.llm_started_agents == [streamed_agent] + assert streamed_run_hooks.llm_ended_agents == [streamed_agent] + assert streamed_agent_hooks.started_agents == [streamed_agent] + assert streamed_agent_hooks.ended_agents == [streamed_agent] + assert streamed_agent_hooks.llm_started_agents == [streamed_agent] + assert streamed_agent_hooks.llm_ended_agents == [streamed_agent] + assert all(item.agent is streamed_agent for item in streamed_result.new_items) + assert run_item_events + assert all(event.item.agent is streamed_agent for event in run_item_events) diff --git a/tests/test_sandbox_session_manager.py b/tests/test_sandbox_session_manager.py new file mode 100644 index 0000000000..c7fc424a12 --- /dev/null +++ b/tests/test_sandbox_session_manager.py @@ -0,0 +1,231 @@ +from __future__ import annotations + +import asyncio +import uuid +from pathlib import Path + +import pytest + +from agents.sandbox.manifest import Manifest +from agents.sandbox.runtime_session_manager import SandboxRuntimeSessionManager +from agents.sandbox.sandboxes.unix_local import ( + UnixLocalSandboxSession, + UnixLocalSandboxSessionState, +) +from agents.sandbox.session import ( + CallbackSink, + EventPayloadPolicy, + Instrumentation, + UCEvent, + UCFinishEvent, +) +from agents.sandbox.session.sinks import ChainedSink, EventSink +from agents.sandbox.snapshot import LocalSnapshot, LocalSnapshotSpec, NoopSnapshotSpec + + +class _EventSink(EventSink): + def __init__(self, *, mode: str, on_error: str = "raise") -> None: + self.mode = mode # type: ignore[assignment] + self.on_error = on_error # type: ignore[assignment] + self.payload_policy = None + + async def handle(self, event: UCEvent) -> None: # pragma: no cover + _ = event + raise NotImplementedError + + +def _build_session(tmp_path: Path) -> UnixLocalSandboxSession: + state = UnixLocalSandboxSessionState( + manifest=Manifest(root=str(tmp_path / "workspace")), + snapshot=LocalSnapshot(id="x", base_path=tmp_path), + ) + return UnixLocalSandboxSession.from_state(state) + + +@pytest.mark.asyncio +async def test_instrumentation_per_op_policy_overrides_default(tmp_path: Path) -> None: + events: list[UCEvent] = [] + session = _build_session(tmp_path) + sink = CallbackSink(lambda event, _session: events.append(event), mode="sync") + sink.bind(session) + instrumentation = Instrumentation( + sinks=[sink], + payload_policy=EventPayloadPolicy(include_exec_output=False), + payload_policy_by_op={"exec": EventPayloadPolicy(include_exec_output=True)}, + ) + + event = UCFinishEvent( + session_id=uuid.uuid4(), + seq=1, + op="exec", + span_id=uuid.uuid4(), + ok=True, + duration_ms=0.0, + ) + event.stdout_bytes = b"hello" + event.stderr_bytes = b"" + + await instrumentation.emit(event) + + assert isinstance(events[0], UCFinishEvent) + assert events[0].stdout == "hello" + + +@pytest.mark.asyncio +async def test_instrumentation_per_sink_policy_overrides_per_op(tmp_path: Path) -> None: + first: list[UCEvent] = [] + second: list[UCEvent] = [] + session = _build_session(tmp_path) + sink_a = CallbackSink(lambda event, _session: first.append(event), mode="sync") + sink_b = CallbackSink( + lambda event, _session: second.append(event), + mode="sync", + payload_policy=EventPayloadPolicy(include_exec_output=True), + ) + sink_a.bind(session) + sink_b.bind(session) + + instrumentation = Instrumentation( + sinks=[sink_a, sink_b], + payload_policy=EventPayloadPolicy(include_exec_output=False), + payload_policy_by_op={"exec": EventPayloadPolicy(include_exec_output=False)}, + ) + + event = UCFinishEvent( + session_id=uuid.uuid4(), + seq=1, + op="exec", + span_id=uuid.uuid4(), + ok=True, + duration_ms=0.0, + ) + event.stdout_bytes = b"hello" + event.stderr_bytes = b"" + + await instrumentation.emit(event) + + assert isinstance(first[0], UCFinishEvent) + assert isinstance(second[0], UCFinishEvent) + assert first[0].stdout is None + assert second[0].stdout == "hello" + + +@pytest.mark.asyncio +async def test_instrumentation_redacts_raw_exec_bytes_when_output_disabled( + tmp_path: Path, +) -> None: + events: list[UCEvent] = [] + session = _build_session(tmp_path) + sink = CallbackSink(lambda event, _session: events.append(event), mode="sync") + sink.bind(session) + instrumentation = Instrumentation( + sinks=[sink], + payload_policy=EventPayloadPolicy(include_exec_output=False), + ) + + event = UCFinishEvent( + session_id=uuid.uuid4(), + seq=1, + op="exec", + span_id=uuid.uuid4(), + ok=True, + duration_ms=0.0, + ) + event.stdout_bytes = b"secret" + event.stderr_bytes = b"secret2" + + await instrumentation.emit(event) + + assert isinstance(events[0], UCFinishEvent) + assert events[0].stdout_bytes is None + assert events[0].stderr_bytes is None + + +@pytest.mark.asyncio +async def test_chained_sink_preserves_completion_order_across_modes() -> None: + completed = asyncio.Event() + + class SlowBestEffortSink(_EventSink): + async def handle(self, event: UCEvent) -> None: + _ = event + await asyncio.sleep(0) + completed.set() + + class AssertAfterSink(_EventSink): + async def handle(self, event: UCEvent) -> None: + _ = event + assert completed.is_set(), "later sink ran before earlier sink completed" + + sink_a = SlowBestEffortSink(mode="best_effort", on_error="raise") + sink_b = AssertAfterSink(mode="sync", on_error="raise") + instrumentation = Instrumentation(sinks=[ChainedSink(sink_a, sink_b)]) + + event = UCFinishEvent( + session_id=uuid.uuid4(), + seq=1, + op="running", + span_id=uuid.uuid4(), + ok=True, + duration_ms=0.0, + ) + await instrumentation.emit(event) + + +@pytest.mark.asyncio +async def test_async_sink_raise_propagates_to_emit() -> None: + class _FailingAsyncSink(_EventSink): + async def handle(self, event: UCEvent) -> None: + _ = event + await asyncio.sleep(0) + raise RuntimeError("boom") + + instrumentation = Instrumentation(sinks=[_FailingAsyncSink(mode="async", on_error="raise")]) + event = UCFinishEvent( + session_id=uuid.uuid4(), + seq=1, + op="running", + span_id=uuid.uuid4(), + ok=True, + duration_ms=0.0, + ) + + with pytest.raises(RuntimeError, match="boom"): + await instrumentation.emit(event) + + +def test_session_manager_uses_custom_snapshot_spec_without_resolving_default( + monkeypatch: pytest.MonkeyPatch, +) -> None: + called = False + + def _unexpected_default_resolution() -> LocalSnapshotSpec: + nonlocal called + called = True + raise AssertionError("default snapshot resolution should not run") + + monkeypatch.setattr( + "agents.sandbox.runtime_session_manager.resolve_default_local_snapshot_spec", + _unexpected_default_resolution, + ) + + custom = LocalSnapshotSpec(base_path=Path("/tmp/custom-sandbox-snapshots")) + resolved = SandboxRuntimeSessionManager._resolve_snapshot_spec(custom) + + assert resolved is custom + assert called is False + + +def test_session_manager_falls_back_to_noop_when_default_snapshot_resolution_fails( + monkeypatch: pytest.MonkeyPatch, +) -> None: + def _raise_os_error() -> LocalSnapshotSpec: + raise OSError("read-only home") + + monkeypatch.setattr( + "agents.sandbox.runtime_session_manager.resolve_default_local_snapshot_spec", + _raise_os_error, + ) + + resolved = SandboxRuntimeSessionManager._resolve_snapshot_spec(None) + + assert isinstance(resolved, NoopSnapshotSpec) diff --git a/tests/test_sandbox_session_sinks.py b/tests/test_sandbox_session_sinks.py new file mode 100644 index 0000000000..9a0f4560b5 --- /dev/null +++ b/tests/test_sandbox_session_sinks.py @@ -0,0 +1,263 @@ +from __future__ import annotations + +import asyncio +import io +import json +import tarfile +import uuid +from pathlib import Path + +import pytest + +from agents.sandbox.manifest import Manifest +from agents.sandbox.sandboxes.unix_local import ( + UnixLocalSandboxSession, + UnixLocalSandboxSessionState, +) +from agents.sandbox.session import ( + CallbackSink, + ChainedSink, + EventPayloadPolicy, + Instrumentation, + JsonlOutboxSink, + SandboxSession, + UCEvent, + UCFinishEvent, + UCStartEvent, + WorkspaceJsonlSink, +) +from agents.sandbox.session.base_sandbox_session import BaseSandboxSession +from agents.sandbox.snapshot import LocalSnapshot + + +def _build_unix_local_session(tmp_path: Path) -> UnixLocalSandboxSession: + workspace = tmp_path / "workspace" + snapshot = LocalSnapshot(id=str(uuid.uuid4()), base_path=tmp_path) + state = UnixLocalSandboxSessionState( + manifest=Manifest(root=str(workspace)), + snapshot=snapshot, + ) + return UnixLocalSandboxSession.from_state(state) + + +@pytest.mark.asyncio +async def test_sandbox_session_exec_emits_stdout_when_enabled(tmp_path: Path) -> None: + events: list[UCEvent] = [] + instrumentation = Instrumentation( + sinks=[CallbackSink(lambda e, _sess: events.append(e), mode="sync")], + payload_policy=EventPayloadPolicy(include_exec_output=True), + ) + + inner = _build_unix_local_session(tmp_path) + async with SandboxSession(inner, instrumentation=instrumentation) as session: + result = await session.exec("echo hi") + assert result.ok() + + exec_finish = [event for event in events if event.op == "exec" and event.phase == "finish"][0] + assert isinstance(exec_finish, UCFinishEvent) + assert exec_finish.stdout is not None + assert "hi" in exec_finish.stdout + + +@pytest.mark.asyncio +async def test_sandbox_session_write_does_not_include_bytes_when_disabled( + tmp_path: Path, +) -> None: + events: list[UCEvent] = [] + instrumentation = Instrumentation( + sinks=[CallbackSink(lambda e, _sess: events.append(e), mode="sync")], + payload_policy=EventPayloadPolicy(include_write_len=False), + ) + + inner = _build_unix_local_session(tmp_path) + async with SandboxSession(inner, instrumentation=instrumentation) as session: + await session.write(Path("x.txt"), io.BytesIO(b"hello")) + + write_start = [event for event in events if event.op == "write" and event.phase == "start"][0] + assert "bytes" not in write_start.data + + +@pytest.mark.asyncio +async def test_jsonl_outbox_sink_appends_one_line_per_event(tmp_path: Path) -> None: + outbox = tmp_path / "events.jsonl" + sink = JsonlOutboxSink(outbox, mode="sync", on_error="raise") + + start_event = UCStartEvent( + session_id=uuid.uuid4(), + seq=1, + op="write", + span_id=uuid.uuid4(), + ) + finish_event = UCFinishEvent( + session_id=start_event.session_id, + seq=2, + op="write", + span_id=start_event.span_id, + ok=True, + duration_ms=0.0, + ) + + await sink.handle(start_event) + await sink.handle(finish_event) + + lines = outbox.read_text(encoding="utf-8").splitlines() + assert len(lines) == 2 + assert json.loads(lines[0])["phase"] == "start" + assert json.loads(lines[1])["phase"] == "finish" + + +@pytest.mark.asyncio +async def test_chained_sink_runs_in_order(tmp_path: Path) -> None: + outbox = tmp_path / "events.jsonl" + seen: list[int] = [] + + def _callback(_event: UCEvent, _session: BaseSandboxSession) -> None: + seen.append(len(outbox.read_text(encoding="utf-8").splitlines())) + + inner = _build_unix_local_session(tmp_path) + callback_sink = CallbackSink(_callback, mode="sync") + callback_sink.bind(inner) + + instrumentation = Instrumentation( + sinks=[ + ChainedSink( + JsonlOutboxSink(outbox, mode="sync", on_error="raise"), + callback_sink, + ) + ] + ) + + start_event = UCStartEvent( + session_id=uuid.uuid4(), + seq=1, + op="write", + span_id=uuid.uuid4(), + ) + finish_event = UCFinishEvent( + session_id=start_event.session_id, + seq=2, + op="write", + span_id=start_event.span_id, + ok=True, + duration_ms=0.0, + ) + + await instrumentation.emit(start_event) + await instrumentation.emit(finish_event) + + assert seen == [1, 2] + + +@pytest.mark.asyncio +async def test_workspace_jsonl_sink_writes_into_workspace_and_persists(tmp_path: Path) -> None: + inner = _build_unix_local_session(tmp_path) + instrumentation = Instrumentation( + sinks=[WorkspaceJsonlSink(mode="sync", on_error="raise", ephemeral=False)] + ) + wrapped = SandboxSession(inner, instrumentation=instrumentation) + + async with wrapped as session: + await session.exec("echo hi") + + outbox_stream = await inner.read(Path(f"logs/events-{inner.state.session_id}.jsonl")) + lines = outbox_stream.read().decode("utf-8").splitlines() + assert any(json.loads(line)["op"] == "exec" for line in lines) + + snapshot_path = tmp_path / f"{inner.state.snapshot.id}.tar" + with tarfile.open(snapshot_path, mode="r:*") as tar: + names = [member.name for member in tar.getmembers()] + assert any(f"logs/events-{inner.state.session_id}.jsonl" in name for name in names) + + +@pytest.mark.asyncio +async def test_workspace_jsonl_sink_supports_session_id_template(tmp_path: Path) -> None: + inner = _build_unix_local_session(tmp_path) + relpath = Path("logs/events-{session_id}.jsonl") + instrumentation = Instrumentation( + sinks=[ + WorkspaceJsonlSink( + mode="sync", + on_error="raise", + ephemeral=False, + workspace_relpath=relpath, + ) + ] + ) + wrapped = SandboxSession(inner, instrumentation=instrumentation) + + async with wrapped as session: + await session.exec("echo hi") + + expected_path = Path(f"logs/events-{inner.state.session_id}.jsonl") + outbox_stream = await inner.read(expected_path) + lines = outbox_stream.read().decode("utf-8").splitlines() + assert any(json.loads(line)["op"] == "exec" for line in lines) + + +@pytest.mark.asyncio +async def test_workspace_jsonl_sink_flushes_on_stop_when_flush_every_gt_one( + tmp_path: Path, +) -> None: + inner = _build_unix_local_session(tmp_path) + instrumentation = Instrumentation( + sinks=[ + WorkspaceJsonlSink( + mode="sync", + on_error="raise", + ephemeral=False, + flush_every=10, + ) + ] + ) + wrapped = SandboxSession(inner, instrumentation=instrumentation) + + async with wrapped as session: + await session.exec("echo hi") + + outbox_stream = await inner.read(Path(f"logs/events-{inner.state.session_id}.jsonl")) + lines = outbox_stream.read().decode("utf-8").splitlines() + assert lines + + snapshot_path = tmp_path / f"{inner.state.snapshot.id}.tar" + with tarfile.open(snapshot_path, mode="r:*") as tar: + names = [member.name for member in tar.getmembers()] + assert any(f"logs/events-{inner.state.session_id}.jsonl" in name for name in names) + + +@pytest.mark.asyncio +async def test_callback_sink_receives_bound_inner_session(tmp_path: Path) -> None: + inner = _build_unix_local_session(tmp_path) + seen: list[tuple[str, BaseSandboxSession]] = [] + + def _callback(event: UCEvent, session: BaseSandboxSession) -> None: + seen.append((event.op, session)) + + instrumentation = Instrumentation(sinks=[CallbackSink(_callback, mode="sync")]) + wrapped = SandboxSession(inner, instrumentation=instrumentation) + + async with wrapped as session: + await session.exec("echo hi") + + assert seen + assert all(session is inner for _op, session in seen) + + +@pytest.mark.asyncio +async def test_sandbox_session_aclose_flushes_best_effort_sink_tasks(tmp_path: Path) -> None: + inner = _build_unix_local_session(tmp_path) + seen: list[tuple[str, str]] = [] + + async def _callback(event: UCEvent, _session: BaseSandboxSession) -> None: + await asyncio.sleep(0) + seen.append((event.op, event.phase)) + + instrumentation = Instrumentation( + sinks=[CallbackSink(_callback, mode="best_effort", on_error="log")] + ) + wrapped = SandboxSession(inner, instrumentation=instrumentation) + + await wrapped.start() + await wrapped.aclose() + + assert ("stop", "finish") in seen + assert ("shutdown", "finish") in seen diff --git a/tests/test_sandbox_session_utils.py b/tests/test_sandbox_session_utils.py new file mode 100644 index 0000000000..4cf0e8f444 --- /dev/null +++ b/tests/test_sandbox_session_utils.py @@ -0,0 +1,165 @@ +from __future__ import annotations + +import io +import shlex +import uuid +from pathlib import Path + +import pytest + +from agents.sandbox.files import EntryKind, FileEntry +from agents.sandbox.manifest import Manifest +from agents.sandbox.session import UCStartEvent +from agents.sandbox.session.base_sandbox_session import BaseSandboxSession +from agents.sandbox.session.events import UCFinishEvent +from agents.sandbox.session.sandbox_session_state import SandboxSessionState +from agents.sandbox.session.utils import ( + _best_effort_stream_len, + _safe_decode, + event_to_json_line, +) +from agents.sandbox.snapshot import NoopSnapshot +from agents.sandbox.types import ExecResult, Permissions + + +class _CaptureExecSession(BaseSandboxSession): + def __init__(self) -> None: + self.state = SandboxSessionState( + manifest=Manifest(), + snapshot=NoopSnapshot(id="noop"), + ) + self.last_command: tuple[str, ...] | None = None + + async def _exec_internal( + self, + *command: str | Path, + timeout: float | None = None, + ) -> ExecResult: + _ = timeout + self.last_command = tuple(str(part) for part in command) + return ExecResult(stdout=b"", stderr=b"", exit_code=0) + + async def read(self, path: Path) -> io.IOBase: + _ = path + raise AssertionError("read() should not be called in this test") + + async def write(self, path: Path, data: io.IOBase) -> None: + _ = (path, data) + raise AssertionError("write() should not be called in this test") + + async def running(self) -> bool: + return True + + async def persist_workspace(self) -> io.IOBase: + return io.BytesIO() + + async def hydrate_workspace(self, data: io.IOBase) -> None: + _ = data + + async def shutdown(self) -> None: + return + + +def test_safe_decode_truncates_and_appends_ellipsis() -> None: + assert _safe_decode(b"abcdef", max_chars=3) == "abc…" + + +def test_best_effort_stream_len_tracks_remaining_bytes_for_seekable_streams() -> None: + buffer = io.BytesIO(b"hello") + assert _best_effort_stream_len(buffer) == 5 + assert buffer.read(1) == b"h" + assert _best_effort_stream_len(buffer) == 4 + + +class _NoSeekableMethodStream(io.IOBase): + def __init__(self, payload: bytes) -> None: + self._buffer = io.BytesIO(payload) + + def tell(self) -> int: + return self._buffer.tell() + + def seek(self, offset: int, whence: int = io.SEEK_SET) -> int: + return self._buffer.seek(offset, whence) + + +def test_best_effort_stream_len_handles_streams_without_seekable_method() -> None: + stream = _NoSeekableMethodStream(b"hello") + + assert _best_effort_stream_len(stream) == 5 + stream.seek(2) + assert _best_effort_stream_len(stream) == 3 + + +def test_event_to_json_line_is_single_line() -> None: + event = UCStartEvent( + session_id=uuid.uuid4(), + seq=1, + op="write", + span_id=uuid.uuid4(), + data={"x": 1}, + ) + + line = event_to_json_line(event) + assert line.endswith("\n") + assert "\n" not in line[:-1] + + +def test_uc_finish_event_excludes_raw_bytes_from_json_dump() -> None: + event = UCFinishEvent( + session_id=uuid.uuid4(), + seq=1, + op="exec", + span_id=uuid.uuid4(), + ok=True, + duration_ms=0.0, + ) + event.stdout_bytes = b"secret" + event.stderr_bytes = b"secret2" + + dumped = event.model_dump(mode="json") + assert "stdout_bytes" not in dumped + assert "stderr_bytes" not in dumped + + +def test_file_entry_is_dir_uses_kind() -> None: + directory_entry = FileEntry( + path="/workspace/dir", + permissions=Permissions.from_str("drwxr-xr-x"), + owner="root", + group="root", + size=0, + kind=EntryKind.DIRECTORY, + ) + file_entry = FileEntry( + path="/workspace/file.txt", + permissions=Permissions.from_str("-rw-r--r--"), + owner="root", + group="root", + size=3, + kind=EntryKind.FILE, + ) + + assert directory_entry.is_dir() is True + assert file_entry.is_dir() is False + + +@pytest.mark.asyncio +async def test_exec_shell_true_quotes_multi_arg_commands() -> None: + session = _CaptureExecSession() + + await session.exec("printf", "%s\n", "hello world", "$(whoami)", "semi;colon", shell=True) + + assert session.last_command == ( + "sh", + "-lc", + shlex.join(["printf", "%s\n", "hello world", "$(whoami)", "semi;colon"]), + ) + + +@pytest.mark.asyncio +async def test_exec_shell_true_preserves_single_shell_snippet() -> None: + session = _CaptureExecSession() + + await session.exec("echo hello && echo goodbye", shell=True) + + assert session.last_command == ("sh", "-lc", "echo hello && echo goodbye") diff --git a/tests/test_sandbox_snapshot.py b/tests/test_sandbox_snapshot.py new file mode 100644 index 0000000000..629bebad93 --- /dev/null +++ b/tests/test_sandbox_snapshot.py @@ -0,0 +1,153 @@ +from __future__ import annotations + +import io +from pathlib import Path +from typing import Literal + +import pytest + +from agents.sandbox.manifest import Manifest +from agents.sandbox.session.base_sandbox_session import BaseSandboxSession +from agents.sandbox.session.sandbox_session_state import SandboxSessionState +from agents.sandbox.snapshot import NoopSnapshot, SnapshotBase +from agents.sandbox.types import ExecResult + + +class TestNoopSnapshot(SnapshotBase): + __test__ = False + type: Literal["test-noop"] = "test-noop" + + async def persist(self, data: io.IOBase) -> None: + _ = data + + async def restore(self) -> io.IOBase: + raise FileNotFoundError(Path("")) + + async def restorable(self) -> bool: + return False + + +def test_sandbox_session_state_roundtrip_preserves_custom_snapshot_type() -> None: + state = SandboxSessionState( + manifest=Manifest(), + snapshot=TestNoopSnapshot(id="custom-snapshot"), + ) + + payload = state.model_dump_json() + restored = SandboxSessionState.model_validate_json(payload) + + assert isinstance(restored.snapshot, TestNoopSnapshot) + assert restored.snapshot.id == "custom-snapshot" + + +def test_snapshot_parse_uses_registered_custom_snapshot_type() -> None: + parsed = SnapshotBase.parse({"type": "test-noop", "id": "registered"}) + + assert isinstance(parsed, TestNoopSnapshot) + assert parsed.id == "registered" + + +def test_duplicate_snapshot_type_registration_raises() -> None: + class TestDuplicateSnapshotA(SnapshotBase): + __test__ = False + type: Literal["test-duplicate"] = "test-duplicate" + + async def persist(self, data: io.IOBase) -> None: + _ = data + + async def restore(self) -> io.IOBase: + raise FileNotFoundError(Path("")) + + async def restorable(self) -> bool: + return False + + _ = TestDuplicateSnapshotA + + with pytest.raises(TypeError, match="already registered"): + + class TestDuplicateSnapshotB(SnapshotBase): + __test__ = False + type: Literal["test-duplicate"] = "test-duplicate" + + async def persist(self, data: io.IOBase) -> None: + _ = data + + async def restore(self) -> io.IOBase: + raise FileNotFoundError(Path("")) + + async def restorable(self) -> bool: + return False + + +def test_snapshot_subclasses_require_type_discriminator_default() -> None: + with pytest.raises(TypeError, match="must define a non-empty string default for `type`"): + + class TestMissingTypeSnapshot(SnapshotBase): + __test__ = False + + async def persist(self, data: io.IOBase) -> None: + _ = data + + async def restore(self) -> io.IOBase: + raise FileNotFoundError(Path("")) + + async def restorable(self) -> bool: + return False + + +class _PersistTrackingSession(BaseSandboxSession): + def __init__(self, snapshot: SnapshotBase) -> None: + self.state = SandboxSessionState( + manifest=Manifest(), + snapshot=snapshot, + ) + self.persist_workspace_calls = 0 + self.persist_payload = b"tracked" + + async def _exec_internal( + self, + *command: str | Path, + timeout: float | None = None, + ) -> ExecResult: + _ = (command, timeout) + raise AssertionError("_exec_internal() should not be called in this test") + + async def read(self, path: Path) -> io.IOBase: + _ = path + raise AssertionError("read() should not be called in this test") + + async def write(self, path: Path, data: io.IOBase) -> None: + _ = (path, data) + raise AssertionError("write() should not be called in this test") + + async def running(self) -> bool: + return True + + async def persist_workspace(self) -> io.IOBase: + self.persist_workspace_calls += 1 + return io.BytesIO(self.persist_payload) + + async def hydrate_workspace(self, data: io.IOBase) -> None: + _ = data + + async def shutdown(self) -> None: + return + + +@pytest.mark.asyncio +async def test_noop_snapshot_stop_skips_workspace_persist() -> None: + session = _PersistTrackingSession(NoopSnapshot(id="noop")) + + await session.stop() + + assert session.persist_workspace_calls == 0 + + +@pytest.mark.asyncio +async def test_non_noop_snapshot_stop_persists_workspace() -> None: + snapshot = TestNoopSnapshot(id="custom-snapshot") + session = _PersistTrackingSession(snapshot) + + await session.stop() + + assert session.persist_workspace_calls == 1 diff --git a/tests/test_sandbox_snapshot_defaults.py b/tests/test_sandbox_snapshot_defaults.py new file mode 100644 index 0000000000..f752b4f859 --- /dev/null +++ b/tests/test_sandbox_snapshot_defaults.py @@ -0,0 +1,105 @@ +from __future__ import annotations + +import os +from pathlib import Path + +from agents.sandbox.snapshot import LocalSnapshotSpec +from agents.sandbox.snapshot_defaults import ( + _DEFAULT_LOCAL_SNAPSHOT_TTL_SECONDS, + cleanup_stale_default_local_snapshots, + default_local_snapshot_base_dir, + resolve_default_local_snapshot_spec, +) + + +def test_default_local_snapshot_base_dir_uses_xdg_state_home(tmp_path: Path) -> None: + state_home = tmp_path / "state" + result = default_local_snapshot_base_dir( + home=tmp_path / "home", + env={"XDG_STATE_HOME": str(state_home)}, + platform="linux", + os_name="posix", + ) + + assert result == state_home / "openai-agents-python" / "sandbox" / "snapshots" + + +def test_default_local_snapshot_base_dir_uses_macos_application_support(tmp_path: Path) -> None: + home = tmp_path / "home" + result = default_local_snapshot_base_dir( + home=home, + env={}, + platform="darwin", + os_name="posix", + ) + + assert ( + result + == home + / "Library" + / "Application Support" + / "openai-agents-python" + / "sandbox" + / "snapshots" + ) + + +def test_default_local_snapshot_base_dir_uses_localappdata_on_windows(tmp_path: Path) -> None: + local_app_data = tmp_path / "LocalAppData" + result = default_local_snapshot_base_dir( + home=tmp_path / "home", + env={"LOCALAPPDATA": str(local_app_data)}, + platform="win32", + os_name="nt", + ) + + assert result == local_app_data / "openai-agents-python" / "sandbox" / "snapshots" + + +def test_cleanup_stale_default_local_snapshots_removes_only_old_tar_files(tmp_path: Path) -> None: + managed_dir = tmp_path / "snapshots" + managed_dir.mkdir() + stale = managed_dir / "stale.tar" + fresh = managed_dir / "fresh.tar" + keep = managed_dir / "keep.txt" + stale.write_bytes(b"stale") + fresh.write_bytes(b"fresh") + keep.write_text("keep") + + now = 2_000_000_000.0 + stale_mtime = now - (_DEFAULT_LOCAL_SNAPSHOT_TTL_SECONDS + 60) + fresh_mtime = now - 60 + os.utime(stale, (stale_mtime, stale_mtime)) + os.utime(fresh, (fresh_mtime, fresh_mtime)) + + cleanup_stale_default_local_snapshots(managed_dir, now=now) + + assert not stale.exists() + assert fresh.exists() + assert keep.exists() + + +def test_resolve_default_local_snapshot_spec_keeps_existing_stale_files( + tmp_path: Path, +) -> None: + state_home = tmp_path / "state" + managed_dir = state_home / "openai-agents-python" / "sandbox" / "snapshots" + managed_dir.mkdir(parents=True) + stale = managed_dir / "stale.tar" + stale.write_bytes(b"stale") + now = 2_000_000_000.0 + stale_mtime = now - (_DEFAULT_LOCAL_SNAPSHOT_TTL_SECONDS + 60) + os.utime(stale, (stale_mtime, stale_mtime)) + + spec = resolve_default_local_snapshot_spec( + home=tmp_path / "home", + env={"XDG_STATE_HOME": str(state_home)}, + platform="linux", + os_name="posix", + now=now, + ) + + assert isinstance(spec, LocalSnapshotSpec) + assert spec.base_path == managed_dir + assert managed_dir.exists() + assert stale.exists() diff --git a/tests/test_server_conversation_tracker.py b/tests/test_server_conversation_tracker.py index baafac6fda..4ebbda7edd 100644 --- a/tests/test_server_conversation_tracker.py +++ b/tests/test_server_conversation_tracker.py @@ -10,6 +10,7 @@ from agents.result import RunResultStreaming from agents.run_config import ModelInputData, RunConfig from agents.run_context import RunContextWrapper +from agents.run_internal.agent_bindings import bind_public_agent from agents.run_internal.oai_conversation import OpenAIServerConversationTracker from agents.run_internal.run_loop import get_new_response, run_single_turn_streamed from agents.run_internal.tool_use_tracker import AgentToolUseTracker @@ -646,7 +647,7 @@ def _filter_input(payload: Any) -> ModelInputData: run_config = RunConfig(call_model_input_filter=_filter_input) await get_new_response( - agent, + bind_public_agent(agent), None, [item_1, item_2], None, @@ -705,7 +706,7 @@ def _filter_input(payload: Any) -> ModelInputData: await run_single_turn_streamed( streamed_result, - agent, + bind_public_agent(agent), RunHooks(), context_wrapper, run_config, @@ -780,7 +781,7 @@ def _filter_input(payload: Any) -> ModelInputData: await run_single_turn_streamed( streamed_result, - agent, + bind_public_agent(agent), RunHooks(), context_wrapper, run_config, diff --git a/tests/test_shell_tool.py b/tests/test_shell_tool.py index b513388d37..8a6a6ff857 100644 --- a/tests/test_shell_tool.py +++ b/tests/test_shell_tool.py @@ -204,7 +204,7 @@ async def test_execute_shell_calls_surfaces_missing_local_executor() -> None: context_wrapper: RunContextWrapper[Any] = RunContextWrapper(context=None) result = await execute_shell_calls( - agent=agent, + public_agent=agent, calls=[tool_run], context_wrapper=context_wrapper, hooks=RunHooks[Any](), diff --git a/tests/test_tool_use_tracker.py b/tests/test_tool_use_tracker.py index d2276c852d..9e6cf4c850 100644 --- a/tests/test_tool_use_tracker.py +++ b/tests/test_tool_use_tracker.py @@ -39,6 +39,59 @@ def test_tool_use_tracker_from_and_serialize_snapshots() -> None: assert serialize_tool_use_tracker(runtime_tracker) == {"serialize-agent": ["one", "two"]} +def test_serialize_and_hydrate_tool_use_tracker_preserves_duplicate_agent_identity() -> None: + second = Agent(name="duplicate") + first = Agent(name="duplicate", handoffs=[second]) + second.handoffs = [first] + + tracker = AgentToolUseTracker() + tracker.add_tool_use(second, ["approval_tool"]) + + snapshot = serialize_tool_use_tracker(tracker, starting_agent=first) + assert snapshot == {"duplicate#2": ["approval_tool"]} + + class _RunState: + def get_tool_use_tracker_snapshot(self) -> dict[str, list[str]]: + return snapshot + + hydrated = AgentToolUseTracker() + hydrate_tool_use_tracker( + tool_use_tracker=hydrated, + run_state=_RunState(), + starting_agent=first, + ) + + assert hydrated.agent_to_tools == [(second, ["approval_tool"])] + + +def test_tool_use_tracker_handles_literal_suffix_names_without_collision() -> None: + literal_suffix = Agent(name="sandbox#2") + first = Agent(name="sandbox", handoffs=[literal_suffix]) + second = Agent(name="sandbox") + literal_suffix.handoffs = [first, second] + first.handoffs = [literal_suffix, second] + second.handoffs = [first, literal_suffix] + + tracker = AgentToolUseTracker() + tracker.add_tool_use(second, ["approval_tool"]) + + snapshot = serialize_tool_use_tracker(tracker, starting_agent=first) + assert snapshot == {"sandbox#3": ["approval_tool"]} + + class _RunState: + def get_tool_use_tracker_snapshot(self) -> dict[str, list[str]]: + return snapshot + + hydrated = AgentToolUseTracker() + hydrate_tool_use_tracker( + tool_use_tracker=hydrated, + run_state=_RunState(), + starting_agent=first, + ) + + assert hydrated.agent_to_tools == [(second, ["approval_tool"])] + + def test_record_used_tools_uses_trace_names_for_namespaced_and_deferred_functions() -> None: agent = Agent(name="tracked-agent") tracker = AgentToolUseTracker() diff --git a/uv.lock b/uv.lock index 78531dc890..b0d61b1c6b 100644 --- a/uv.lock +++ b/uv.lock @@ -127,6 +127,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/f5/10/6c25ed6de94c49f88a91fa5018cb4c0f3625f31d5be9f771ebe5cc7cd506/aiosqlite-0.21.0-py3-none-any.whl", hash = "sha256:2549cf4057f95f53dcba16f2b64e8e2791d7e1adedb13197dd8ed77bb226d7d0", size = 15792, upload-time = "2025-02-03T07:30:13.6Z" }, ] +[[package]] +name = "annotated-doc" +version = "0.0.4" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/57/ba/046ceea27344560984e26a590f90bc7f4a75b06701f653222458922b558c/annotated_doc-0.0.4.tar.gz", hash = "sha256:fbcda96e87e9c92ad167c2e53839e57503ecfda18804ea28102353485033faa4", size = 7288, upload-time = "2025-11-10T22:07:42.062Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/1e/d3/26bf1008eb3d2daa8ef4cacc7f3bfdc11818d111f7e2d0201bc6e3b49d45/annotated_doc-0.0.4-py3-none-any.whl", hash = "sha256:571ac1dc6991c450b25a9c2d84a3705e2ae7a53467b5d111c24fa8baabbed320", size = 5303, upload-time = "2025-11-10T22:07:40.673Z" }, +] + [[package]] name = "annotated-types" version = "0.7.0" @@ -253,6 +262,59 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/41/ff/392bff89415399a979be4a65357a41d92729ae8580a66073d8ec8d810f98/backrefs-5.9-py39-none-any.whl", hash = "sha256:f48ee18f6252b8f5777a22a00a09a85de0ca931658f1dd96d4406a34f3748c60", size = 380265, upload-time = "2025-06-22T19:34:12.405Z" }, ] +[[package]] +name = "bracex" +version = "2.6" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/63/9a/fec38644694abfaaeca2798b58e276a8e61de49e2e37494ace423395febc/bracex-2.6.tar.gz", hash = "sha256:98f1347cd77e22ee8d967a30ad4e310b233f7754dbf31ff3fceb76145ba47dc7", size = 26642, upload-time = "2025-06-22T19:12:31.254Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/9d/2a/9186535ce58db529927f6cf5990a849aa9e052eea3e2cfefe20b9e1802da/bracex-2.6-py3-none-any.whl", hash = "sha256:0b0049264e7340b3ec782b5cb99beb325f36c3782a32e36e876452fd49a09952", size = 11508, upload-time = "2025-06-22T19:12:29.781Z" }, +] + +[[package]] +name = "cbor2" +version = "5.8.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/d9/8e/8b4fdde28e42ffcd741a37f4ffa9fb59cd4fe01625b544dfcfd9ccb54f01/cbor2-5.8.0.tar.gz", hash = "sha256:b19c35fcae9688ac01ef75bad5db27300c2537eb4ee00ed07e05d8456a0d4931", size = 107825, upload-time = "2025-12-30T18:44:22.455Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/3c/05/486166d9e998d65d70810e63eeacc8c5f13d167d8797cf2d73a588beb335/cbor2-5.8.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:2263c0c892194f10012ced24c322d025d9d7b11b41da1c357f3b3fe06676e6b7", size = 69882, upload-time = "2025-12-30T18:43:25.365Z" }, + { url = "https://files.pythonhosted.org/packages/4e/d0/ee976eaaf21c211eef651e1a921c109c3c3a3785d98307d74a70d142f341/cbor2-5.8.0-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6ffe4ca079f6f8ed393f5c71a8de22651cb27bd50e74e2bcd6bc9c8f853a732b", size = 260696, upload-time = "2025-12-30T18:43:27.784Z" }, + { url = "https://files.pythonhosted.org/packages/66/7f/81cabd3aee6cc54b101a5214d5c3e541d275d7c05647c7dfc266c6aacf6f/cbor2-5.8.0-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0427bd166230fe4c4b72965c6f2b6273bf29016d97cf08b258fa48db851ea598", size = 252135, upload-time = "2025-12-30T18:43:29.418Z" }, + { url = "https://files.pythonhosted.org/packages/c2/0b/f38e8c579e7e2d88d446549bce35bde7d845199300bc456b4123d6e6f0af/cbor2-5.8.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:c23a04947c37964d70028ca44ea2a8709f09b8adc0090f9b5710fa957e9bc545", size = 255342, upload-time = "2025-12-30T18:43:30.966Z" }, + { url = "https://files.pythonhosted.org/packages/5d/02/8413f1bd42c8f665fb85374151599cb4957848f0f307d08334a08dee544c/cbor2-5.8.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:218d5c7d2e8d13c7eded01a1b3fe2a9a1e51a7a843cefb8d38cb4bbbc6ad9bf7", size = 247191, upload-time = "2025-12-30T18:43:32.555Z" }, + { url = "https://files.pythonhosted.org/packages/e5/b8/edeffcad06b83d3661827973a8e6f5d51a9f5842e1ee9d191fdef60388ad/cbor2-5.8.0-cp310-cp310-win_amd64.whl", hash = "sha256:4ce7d907a25448af7c13415281d739634edfd417228b274309b243ca52ad71f9", size = 69254, upload-time = "2025-12-30T18:43:33.717Z" }, + { url = "https://files.pythonhosted.org/packages/ce/1a/dde6537d8d1c2b3157ea6487ea417a5ad0157687d0e9a3ff806bf23c8cb1/cbor2-5.8.0-cp310-cp310-win_arm64.whl", hash = "sha256:628d0ea850aa040921a0e50a08180e7d20cf691432cec3eabc193f643eccfbde", size = 64946, upload-time = "2025-12-30T18:43:34.849Z" }, + { url = "https://files.pythonhosted.org/packages/88/4b/623435ef9b98e86b6956a41863d39ff4fe4d67983948b5834f55499681dd/cbor2-5.8.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:18ac191640093e6c7fbcb174c006ffec4106c3d8ab788e70272c1c4d933cbe11", size = 69875, upload-time = "2025-12-30T18:43:35.888Z" }, + { url = "https://files.pythonhosted.org/packages/58/17/f664201080b2a7d0f57c16c8e9e5922013b92f202e294863ec7e75b7ff7f/cbor2-5.8.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:fddee9103a17d7bed5753f0c7fc6663faa506eb953e50d8287804eccf7b048e6", size = 268316, upload-time = "2025-12-30T18:43:37.161Z" }, + { url = "https://files.pythonhosted.org/packages/d0/e1/072745b4ff01afe9df2cd627f8fc51a1acedb5d3d1253765625d2929db91/cbor2-5.8.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8d2ea26fad620aba5e88d7541be8b10c5034a55db9a23809b7cb49f36803f05b", size = 258874, upload-time = "2025-12-30T18:43:38.878Z" }, + { url = "https://files.pythonhosted.org/packages/a7/10/61c262b886d22b62c56e8aac6d10fa06d0953c997879ab882a31a624952b/cbor2-5.8.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:de68b4b310b072b082d317adc4c5e6910173a6d9455412e6183d72c778d1f54c", size = 261971, upload-time = "2025-12-30T18:43:40.401Z" }, + { url = "https://files.pythonhosted.org/packages/7e/42/b7862f5e64364b10ad120ea53e87ec7e891fb268cb99c572348e647cf7e9/cbor2-5.8.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:418d2cf0e03e90160fa1474c05a40fe228bbb4a92d1628bdbbd13a48527cb34d", size = 254151, upload-time = "2025-12-30T18:43:41.938Z" }, + { url = "https://files.pythonhosted.org/packages/16/6a/8d3636cf75466c18615e7cfac0d345ee3c030f6c79535faed0c2c02b1839/cbor2-5.8.0-cp311-cp311-win_amd64.whl", hash = "sha256:453200ffa1c285ea46ab5745736a015526d41f22da09cb45594624581d959770", size = 69169, upload-time = "2025-12-30T18:43:43.424Z" }, + { url = "https://files.pythonhosted.org/packages/9b/88/79b205bf869558b39a11de70750cb13679b27ba5654a43bed3f2aee7d1b4/cbor2-5.8.0-cp311-cp311-win_arm64.whl", hash = "sha256:f6615412fca973a8b472b3efc4dab01df71cc13f15d8b2c0a1cffac44500f12d", size = 64955, upload-time = "2025-12-30T18:43:44.7Z" }, + { url = "https://files.pythonhosted.org/packages/2f/4f/3a16e3e8fd7e5fd86751a4f1aad218a8d19a96e75ec3989c3e95a8fe1d8f/cbor2-5.8.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:4b3f91fa699a5ce22470e973601c62dd9d55dc3ca20ee446516ac075fcab27c9", size = 70270, upload-time = "2025-12-30T18:43:46.005Z" }, + { url = "https://files.pythonhosted.org/packages/38/81/0d0cf0796fe8081492a61c45278f03def21a929535a492dd97c8438f5dbe/cbor2-5.8.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:518c118a5e00001854adb51f3164e647aa99b6a9877d2a733a28cb5c0a4d6857", size = 286242, upload-time = "2025-12-30T18:43:47.026Z" }, + { url = "https://files.pythonhosted.org/packages/7b/a9/fdab6c10190cfb8d639e01f2b168f2406fc847a2a6bc00e7de78c3381d0a/cbor2-5.8.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:cff2a1999e49cd51c23d1b6786a012127fd8f722c5946e82bd7ab3eb307443f3", size = 285412, upload-time = "2025-12-30T18:43:48.563Z" }, + { url = "https://files.pythonhosted.org/packages/31/59/746a8e630996217a3afd523f583fcf7e3d16640d63f9a03f0f4e4f74b5b1/cbor2-5.8.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:4c4492160212374973cdc14e46f0565f2462721ef922b40f7ea11e7d613dfb2a", size = 278041, upload-time = "2025-12-30T18:43:49.92Z" }, + { url = "https://files.pythonhosted.org/packages/0f/a3/f3bbeb6dedd45c6e0cddd627ea790dea295eaf82c83f0e2159b733365ebd/cbor2-5.8.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:546c7c7c4c6bcdc54a59242e0e82cea8f332b17b4465ae628718fef1fce401ca", size = 278185, upload-time = "2025-12-30T18:43:51.192Z" }, + { url = "https://files.pythonhosted.org/packages/67/e5/9013d6b857ceb6cdb2851ffb5a887f53f2bab934a528c9d6fa73d9989d84/cbor2-5.8.0-cp312-cp312-win_amd64.whl", hash = "sha256:074f0fa7535dd7fdee247c2c99f679d94f3aa058ccb1ccf4126cc72d6d89cbae", size = 69817, upload-time = "2025-12-30T18:43:52.352Z" }, + { url = "https://files.pythonhosted.org/packages/a8/ab/7aa94ba3d44ecbc3a97bdb2fb6a8298063fe2e0b611e539a6fe41e36da20/cbor2-5.8.0-cp312-cp312-win_arm64.whl", hash = "sha256:f95fed480b2a0d843f294d2a1ef4cc0f6a83c7922927f9f558e1f5a8dc54b7ca", size = 64923, upload-time = "2025-12-30T18:43:53.719Z" }, + { url = "https://files.pythonhosted.org/packages/a6/0d/5a3f20bafaefeb2c1903d961416f051c0950f0d09e7297a3aa6941596b29/cbor2-5.8.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:6d8d104480845e2f28c6165b4c961bbe58d08cb5638f368375cfcae051c28015", size = 70332, upload-time = "2025-12-30T18:43:54.694Z" }, + { url = "https://files.pythonhosted.org/packages/57/66/177a3f089e69db69c987453ab4934086408c3338551e4984734597be9f80/cbor2-5.8.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:43efee947e5ab67d406d6e0dc61b5dee9d2f5e89ae176f90677a3741a20ca2e7", size = 285985, upload-time = "2025-12-30T18:43:55.733Z" }, + { url = "https://files.pythonhosted.org/packages/b7/8e/9e17b8e4ed80a2ce97e2dfa5915c169dbb31599409ddb830f514b57f96cc/cbor2-5.8.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:be7ae582f50be539e09c134966d0fd63723fc4789b8dff1f6c2e3f24ae3eaf32", size = 285173, upload-time = "2025-12-30T18:43:57.321Z" }, + { url = "https://files.pythonhosted.org/packages/cc/33/9f92e107d78f88ac22723ac15d0259d220ba98c1d855e51796317f4c4114/cbor2-5.8.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:50f5c709561a71ea7970b4cd2bf9eda4eccacc0aac212577080fdfe64183e7f5", size = 278395, upload-time = "2025-12-30T18:43:58.497Z" }, + { url = "https://files.pythonhosted.org/packages/2f/3f/46b80050a4a35ce5cf7903693864a9fdea7213567dc8faa6e25cb375c182/cbor2-5.8.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:a6790ecc73aa93e76d2d9076fc42bf91a9e69f2295e5fa702e776dbe986465bd", size = 278330, upload-time = "2025-12-30T18:43:59.656Z" }, + { url = "https://files.pythonhosted.org/packages/eb/d2/d41f8c04c783a4d204e364be2d38043d4f732a3bed6f4c732e321cf34c7b/cbor2-5.8.0-cp313-cp313-win_amd64.whl", hash = "sha256:c114af8099fa65a19a514db87ce7a06e942d8fea2730afd49be39f8e16e7f5e0", size = 69841, upload-time = "2025-12-30T18:44:01.159Z" }, + { url = "https://files.pythonhosted.org/packages/1b/8c/0397a82f6e67665009951453c83058e4c77ba54b9a9017ede56d6870306c/cbor2-5.8.0-cp313-cp313-win_arm64.whl", hash = "sha256:ab3ba00494ad8669a459b12a558448d309c271fa4f89b116ad496ee35db38fea", size = 64982, upload-time = "2025-12-30T18:44:02.138Z" }, + { url = "https://files.pythonhosted.org/packages/4b/0c/0654233d7543ac8a50f4785f172430ddc97538ba418eb305d6e529d1a120/cbor2-5.8.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:ad72381477133046ce217617d839ea4e9454f8b77d9a6351b229e214102daeb7", size = 70710, upload-time = "2025-12-30T18:44:03.209Z" }, + { url = "https://files.pythonhosted.org/packages/84/62/4671d24e557d7f5a74a01b422c538925140c0495e57decde7e566f91d029/cbor2-5.8.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6da25190fad3434ce99876b11d4ca6b8828df6ca232cf7344cd14ae1166fb718", size = 285005, upload-time = "2025-12-30T18:44:05.109Z" }, + { url = "https://files.pythonhosted.org/packages/87/85/0c67d763a08e848c9a80d7e4723ba497cce676f41bc7ca1828ae90a0a872/cbor2-5.8.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c13919e3a24c5a6d286551fa288848a4cedc3e507c58a722ccd134e461217d99", size = 282435, upload-time = "2025-12-30T18:44:06.465Z" }, + { url = "https://files.pythonhosted.org/packages/b2/01/0650972b4dbfbebcfbe37cbba7fc3cd9019a8da6397ab3446e07175e342b/cbor2-5.8.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:f8c40d32e5972047a777f9bf730870828f3cf1c43b3eb96fd0429c57a1d3b9e6", size = 277493, upload-time = "2025-12-30T18:44:07.609Z" }, + { url = "https://files.pythonhosted.org/packages/b3/6c/7704a4f32adc7f10f3b41ec067f500a4458f7606397af5e4cf2d368fd288/cbor2-5.8.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:7627894bc0b3d5d0807f31e3107e11b996205470c4429dc2bb4ef8bfe7f64e1e", size = 276085, upload-time = "2025-12-30T18:44:09.021Z" }, + { url = "https://files.pythonhosted.org/packages/88/6d/e43452347630efe8133f5304127539100d937c138c0996d27ec63963ec2c/cbor2-5.8.0-cp314-cp314-win_amd64.whl", hash = "sha256:b51c5e59becae746ca4de2bbaa8a2f5c64a68fec05cea62941b1a84a8335f7d1", size = 71657, upload-time = "2025-12-30T18:44:10.162Z" }, + { url = "https://files.pythonhosted.org/packages/8b/66/9a780ef34ab10a0437666232e885378cdd5f60197b1b5e61a62499e5a10a/cbor2-5.8.0-cp314-cp314-win_arm64.whl", hash = "sha256:53b630f4db4b9f477ad84077283dd17ecf9894738aa17ef4938c369958e02a71", size = 67171, upload-time = "2025-12-30T18:44:11.619Z" }, + { url = "https://files.pythonhosted.org/packages/d6/4f/101071f880b4da05771128c0b89f41e334cff044dee05fb013c8f4be661c/cbor2-5.8.0-py3-none-any.whl", hash = "sha256:3727d80f539567b03a7aa11890e57798c67092c38df9e6c23abb059e0f65069c", size = 24374, upload-time = "2025-12-30T18:44:21.476Z" }, +] + [[package]] name = "certifi" version = "2025.8.3" @@ -576,6 +638,50 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/e3/26/57c6fb270950d476074c087527a558ccb6f4436657314bfb6cdf484114c4/docker-7.1.0-py3-none-any.whl", hash = "sha256:c96b93b7f0a746f9e77d325bcfb87422a3d8bd4f03136ae8a85b37f1898d5fc0", size = 147774, upload-time = "2024-05-23T11:13:55.01Z" }, ] +[[package]] +name = "dockerfile-parse" +version = "2.0.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/92/df/929ee0b5d2c8bd8d713c45e71b94ab57c7e11e322130724d54f469b2cd48/dockerfile-parse-2.0.1.tar.gz", hash = "sha256:3184ccdc513221983e503ac00e1aa504a2aa8f84e5de673c46b0b6eee99ec7bc", size = 24556, upload-time = "2023-07-18T13:36:07.897Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/7a/6c/79cd5bc1b880d8c1a9a5550aa8dacd57353fa3bb2457227e1fb47383eb49/dockerfile_parse-2.0.1-py2.py3-none-any.whl", hash = "sha256:bdffd126d2eb26acf1066acb54cb2e336682e1d72b974a40894fac76a4df17f6", size = 14845, upload-time = "2023-07-18T13:36:06.052Z" }, +] + +[[package]] +name = "e2b" +version = "2.15.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "attrs" }, + { name = "dockerfile-parse" }, + { name = "httpcore" }, + { name = "httpx" }, + { name = "packaging" }, + { name = "protobuf" }, + { name = "python-dateutil" }, + { name = "rich" }, + { name = "typing-extensions" }, + { name = "wcmatch" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/c2/d9/46cbb319dc8bd8a0428cffce61bdc1c13fbe15390f84cdb6472412e17f3a/e2b-2.15.2.tar.gz", hash = "sha256:414379d2421d6827eeb2eb50a4d6b3fdb7d691b39ff73b5ea05ca4b532819831", size = 139751, upload-time = "2026-03-09T22:22:44.288Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ac/ae/859e500f2a0a419b0d7486d6f18e7e47f81cabd34c2f2e252dd21027ed85/e2b-2.15.2-py3-none-any.whl", hash = "sha256:19a56fbdea25974dc81426ed48337eae6cea91d404f5bcf8861a5a2c6e4d982a", size = 257033, upload-time = "2026-03-09T22:22:42.921Z" }, +] + +[[package]] +name = "e2b-code-interpreter" +version = "2.4.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "attrs" }, + { name = "e2b" }, + { name = "httpx" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/1e/eb/db6e51edd9f3402fd68d026572579b9b1bd833b10d990376a1e4c05d5b8d/e2b_code_interpreter-2.4.1.tar.gz", hash = "sha256:4b15014ee0d0dfcdc3072e1f409cbb87ca48f48d53d75629b7257e5513b9e7dd", size = 10700, upload-time = "2025-11-26T18:12:38.086Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/1b/e7/09b9106ead227f7be14bd97c3181391ee498bb38933b1a9c566b72c8567a/e2b_code_interpreter-2.4.1-py3-none-any.whl", hash = "sha256:15d35f025b4a15033e119f2e12e7ac65657ad2b5a013fa9149e74581fbee778a", size = 13719, upload-time = "2025-11-26T18:12:36.7Z" }, +] + [[package]] name = "eval-type-backport" version = "0.2.2" @@ -1005,6 +1111,19 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/05/18/56999a1da3577d8ccc8698a575d6638e15fe25650cc88b2ce0a087f180b9/grpcio_status-1.67.1-py3-none-any.whl", hash = "sha256:16e6c085950bdacac97c779e6a502ea671232385e6e37f258884d6883392c2bd", size = 14427, upload-time = "2024-10-29T06:27:38.228Z" }, ] +[[package]] +name = "grpclib" +version = "0.4.9" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "h2" }, + { name = "multidict" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/5b/28/5a2c299ec82a876a252c5919aa895a6f1d1d35c96417c5ce4a4660dc3a80/grpclib-0.4.9.tar.gz", hash = "sha256:cc589c330fa81004c6400a52a566407574498cb5b055fa927013361e21466c46", size = 84798, upload-time = "2025-12-14T22:23:14.349Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/5c/90/b0cbbd9efcc82816c58f31a34963071aa19fb792a212a5d9caf8e0fc3097/grpclib-0.4.9-py3-none-any.whl", hash = "sha256:7762ec1c8ed94dfad597475152dd35cbd11aecaaca2f243e29702435ca24cf0e", size = 77063, upload-time = "2025-12-14T22:23:13.224Z" }, +] + [[package]] name = "h11" version = "0.16.0" @@ -1014,6 +1133,19 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/04/4b/29cac41a4d98d144bf5f6d33995617b185d14b22401f75ca86f384e87ff1/h11-0.16.0-py3-none-any.whl", hash = "sha256:63cf8bbe7522de3bf65932fda1d9c2772064ffb3dae62d55932da54b31cb6c86", size = 37515, upload-time = "2025-04-24T03:35:24.344Z" }, ] +[[package]] +name = "h2" +version = "4.3.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "hpack" }, + { name = "hyperframe" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/1d/17/afa56379f94ad0fe8defd37d6eb3f89a25404ffc71d4d848893d270325fc/h2-4.3.0.tar.gz", hash = "sha256:6c59efe4323fa18b47a632221a1888bd7fde6249819beda254aeca909f221bf1", size = 2152026, upload-time = "2025-08-23T18:12:19.778Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/69/b2/119f6e6dcbd96f9069ce9a2665e0146588dc9f88f29549711853645e736a/h2-4.3.0-py3-none-any.whl", hash = "sha256:c438f029a25f7945c69e0ccf0fb951dc3f73a5f6412981daee861431b70e2bdd", size = 61779, upload-time = "2025-08-23T18:12:17.779Z" }, +] + [[package]] name = "hf-xet" version = "1.1.7" @@ -1029,6 +1161,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/a3/73/e354eae84ceff117ec3560141224724794828927fcc013c5b449bf0b8745/hf_xet-1.1.7-cp37-abi3-win_amd64.whl", hash = "sha256:2e356da7d284479ae0f1dea3cf5a2f74fdf925d6dca84ac4341930d892c7cb34", size = 2820008, upload-time = "2025-08-06T00:30:57.056Z" }, ] +[[package]] +name = "hpack" +version = "4.1.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/2c/48/71de9ed269fdae9c8057e5a4c0aa7402e8bb16f2c6e90b3aa53327b113f8/hpack-4.1.0.tar.gz", hash = "sha256:ec5eca154f7056aa06f196a557655c5b009b382873ac8d1e66e79e87535f1dca", size = 51276, upload-time = "2025-01-22T21:44:58.347Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/07/c6/80c95b1b2b94682a72cbdbfb85b81ae2daffa4291fbfa1b1464502ede10d/hpack-4.1.0-py3-none-any.whl", hash = "sha256:157ac792668d995c657d93111f46b4535ed114f0c9c8d672271bbec7eae1b496", size = 34357, upload-time = "2025-01-22T21:44:56.92Z" }, +] + [[package]] name = "httpcore" version = "1.0.9" @@ -1085,6 +1226,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/39/7b/bb06b061991107cd8783f300adff3e7b7f284e330fd82f507f2a1417b11d/huggingface_hub-0.34.4-py3-none-any.whl", hash = "sha256:9b365d781739c93ff90c359844221beef048403f1bc1f1c123c191257c3c890a", size = 561452, upload-time = "2025-08-08T09:14:50.159Z" }, ] +[[package]] +name = "hyperframe" +version = "6.1.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/02/e7/94f8232d4a74cc99514c13a9f995811485a6903d48e5d952771ef6322e30/hyperframe-6.1.0.tar.gz", hash = "sha256:f630908a00854a7adeabd6382b43923a4c4cd4b821fcb527e6ab9e15382a3b08", size = 26566, upload-time = "2025-01-22T21:41:49.302Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/48/30/47d0bf6072f7252e6521f3447ccfa40b421b6824517f82854703d0f5a98b/hyperframe-6.1.0-py3-none-any.whl", hash = "sha256:b03380493a519fce58ea5af42e4a42317bf9bd425596f7a0835ffce80f1a42e5", size = 13007, upload-time = "2025-01-22T21:41:47.295Z" }, +] + [[package]] name = "idna" version = "3.10" @@ -1552,6 +1702,31 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/3b/dd/a24ee3de56954bfafb6ede7cd63c2413bb842cc48eb45e41c43a05a33074/mkdocstrings_python-1.16.12-py3-none-any.whl", hash = "sha256:22ded3a63b3d823d57457a70ff9860d5a4de9e8b1e482876fc9baabaf6f5f374", size = 124287, upload-time = "2025-06-03T12:52:47.819Z" }, ] +[[package]] +name = "modal" +version = "1.3.5" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "aiohttp" }, + { name = "cbor2" }, + { name = "certifi" }, + { name = "click" }, + { name = "grpclib" }, + { name = "protobuf" }, + { name = "rich" }, + { name = "synchronicity" }, + { name = "toml" }, + { name = "typer" }, + { name = "types-certifi" }, + { name = "types-toml" }, + { name = "typing-extensions" }, + { name = "watchfiles" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/32/fd/f4a684209dab54d7dc9d92f48d779b30d04aa8b4c6dd1395d6c61967ee34/modal-1.3.5.tar.gz", hash = "sha256:2e320e7dbc8995ce0769796a9027248a8b976b519469cc4599d6855a1a53a123", size = 655193, upload-time = "2026-03-03T18:13:06.22Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/10/39/aa5c773a4dddef833f1c846bb4204b442588b99a1d15ab7818157e66b32c/modal-1.3.5-py3-none-any.whl", hash = "sha256:67e5d3635c2c355d63b3e30f9012dd2bc9c38d5747349335c7ba9da65edca1cb", size = 755272, upload-time = "2026-03-03T18:13:03.323Z" }, +] + [[package]] name = "multidict" version = "6.6.4" @@ -1905,12 +2080,22 @@ dapr = [ { name = "dapr" }, { name = "grpcio" }, ] +docker = [ + { name = "docker" }, +] +e2b = [ + { name = "e2b" }, + { name = "e2b-code-interpreter" }, +] encrypt = [ { name = "cryptography" }, ] litellm = [ { name = "litellm" }, ] +modal = [ + { name = "modal" }, +] realtime = [ { name = "websockets" }, ] @@ -1968,11 +2153,15 @@ requires-dist = [ { name = "asyncpg", marker = "extra == 'sqlalchemy'", specifier = ">=0.29.0" }, { name = "cryptography", marker = "extra == 'encrypt'", specifier = ">=45.0,<46" }, { name = "dapr", marker = "extra == 'dapr'", specifier = ">=1.16.0" }, + { name = "docker", marker = "extra == 'docker'", specifier = ">=6.1" }, + { name = "e2b", marker = "extra == 'e2b'", specifier = ">=2.12.1" }, + { name = "e2b-code-interpreter", marker = "extra == 'e2b'", specifier = ">=1.0" }, { name = "graphviz", marker = "extra == 'viz'", specifier = ">=0.17" }, { name = "griffe", specifier = ">=1.5.6,<2" }, { name = "grpcio", marker = "extra == 'dapr'", specifier = ">=1.60.0" }, { name = "litellm", marker = "extra == 'litellm'", specifier = ">=1.81.0,<2" }, { name = "mcp", marker = "python_full_version >= '3.10'", specifier = ">=1.19.0,<2" }, + { name = "modal", marker = "extra == 'modal'", specifier = ">=1.3.1" }, { name = "numpy", marker = "python_full_version >= '3.10' and extra == 'voice'", specifier = ">=2.2.0,<3" }, { name = "openai", specifier = ">=2.26.0,<3" }, { name = "pydantic", specifier = ">=2.12.2,<3" }, @@ -1984,7 +2173,7 @@ requires-dist = [ { name = "websockets", marker = "extra == 'realtime'", specifier = ">=15.0,<16" }, { name = "websockets", marker = "extra == 'voice'", specifier = ">=15.0,<16" }, ] -provides-extras = ["voice", "viz", "litellm", "realtime", "sqlalchemy", "encrypt", "redis", "dapr"] +provides-extras = ["voice", "viz", "litellm", "realtime", "sqlalchemy", "encrypt", "redis", "dapr", "docker", "modal", "e2b"] [package.metadata.requires-dev] dev = [ @@ -2011,7 +2200,7 @@ dev = [ { name = "pytest-asyncio" }, { name = "pytest-mock", specifier = ">=3.14.0" }, { name = "pytest-xdist" }, - { name = "rich", specifier = ">=13.1.0,<14" }, + { name = "rich", specifier = ">=13.1.0,<15" }, { name = "ruff", specifier = "==0.9.2" }, { name = "sounddevice" }, { name = "testcontainers", specifier = "==4.12.0" }, @@ -2806,16 +2995,15 @@ wheels = [ [[package]] name = "rich" -version = "13.9.4" +version = "14.3.3" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "markdown-it-py" }, { name = "pygments" }, - { name = "typing-extensions", marker = "python_full_version < '3.11'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/ab/3a/0316b28d0761c6734d6bc14e770d85506c986c85ffb239e688eeaab2c2bc/rich-13.9.4.tar.gz", hash = "sha256:439594978a49a09530cff7ebc4b5c7103ef57baf48d5ea3184f21d9a2befa098", size = 223149, upload-time = "2024-11-01T16:43:57.873Z" } +sdist = { url = "https://files.pythonhosted.org/packages/b3/c6/f3b320c27991c46f43ee9d856302c70dc2d0fb2dba4842ff739d5f46b393/rich-14.3.3.tar.gz", hash = "sha256:b8daa0b9e4eef54dd8cf7c86c03713f53241884e814f4e2f5fb342fe520f639b", size = 230582, upload-time = "2026-02-19T17:23:12.474Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/19/71/39c7c0d87f8d4e6c020a393182060eaefeeae6c01dab6a84ec346f2567df/rich-13.9.4-py3-none-any.whl", hash = "sha256:6049d5e6ec054bf2779ab3358186963bac2ea89175919d699e378b99738c2a90", size = 242424, upload-time = "2024-11-01T16:43:55.817Z" }, + { url = "https://files.pythonhosted.org/packages/14/25/b208c5683343959b670dc001595f2f3737e051da617f66c31f7c4fa93abc/rich-14.3.3-py3-none-any.whl", hash = "sha256:793431c1f8619afa7d3b52b2cdec859562b950ea0d4b6b505397612db8d5362d", size = 310458, upload-time = "2026-02-19T17:23:13.732Z" }, ] [[package]] @@ -2978,6 +3166,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/0e/4e/33df635528292bd2d18404e4daabcd74ca8a9853b2e1df85ed3d32d24362/ruff-0.9.2-py3-none-win_arm64.whl", hash = "sha256:a1b63fa24149918f8b37cef2ee6fff81f24f0d74b6f0bdc37bc3e1f2143e41c6", size = 10001738, upload-time = "2025-01-16T13:22:18.121Z" }, ] +[[package]] +name = "shellingham" +version = "1.5.4" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/58/15/8b3609fd3830ef7b27b655beb4b4e9c62313a4e8da8c676e142cc210d58e/shellingham-1.5.4.tar.gz", hash = "sha256:8dbca0739d487e5bd35ab3ca4b36e11c4078f3a234bfce294b0a0291363404de", size = 10310, upload-time = "2023-10-24T04:13:40.426Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e0/f9/0595336914c5619e5f28a1fb793285925a8cd4b432c9da0a987836c7f822/shellingham-1.5.4-py2.py3-none-any.whl", hash = "sha256:7ecfff8f2fd72616f7481040475a65b2bf8af90a56c89140852d1120324e8686", size = 9755, upload-time = "2023-10-24T04:13:38.866Z" }, +] + [[package]] name = "six" version = "1.17.0" @@ -3090,6 +3287,18 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/f7/1f/b876b1f83aef204198a42dc101613fefccb32258e5428b5f9259677864b4/starlette-0.47.2-py3-none-any.whl", hash = "sha256:c5847e96134e5c5371ee9fac6fdf1a67336d5815e09eb2a01fdb57a351ef915b", size = 72984, upload-time = "2025-07-20T17:31:56.738Z" }, ] +[[package]] +name = "synchronicity" +version = "0.11.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/a3/26/8874d34755691994266d4a844ba8d53d10c2690ec67f246ca4d6b6f34cbb/synchronicity-0.11.1.tar.gz", hash = "sha256:3628df9ab34bd7be89b729104114841c62612c5d5ec43b76f4b7b243185ec1a8", size = 58131, upload-time = "2025-12-19T18:28:42.291Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/3f/b9/71153db12f4ad029cfe9b7fbf9792ef3fc9ade4485d31a13470b52954e62/synchronicity-0.11.1-py3-none-any.whl", hash = "sha256:53959c7f8b9b852fb5ea4d3d290a47a04310ede483a4cf0f8452cb4b5fa09db2", size = 40399, upload-time = "2025-12-19T18:28:40.972Z" }, +] + [[package]] name = "testcontainers" version = "4.12.0" @@ -3208,6 +3417,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/41/f2/fd673d979185f5dcbac4be7d09461cbb99751554ffb6718d0013af8604cb/tokenizers-0.21.4-cp39-abi3-win_amd64.whl", hash = "sha256:475d807a5c3eb72c59ad9b5fcdb254f6e17f53dfcbb9903233b0dfa9c943b597", size = 2507568, upload-time = "2025-07-28T15:48:55.456Z" }, ] +[[package]] +name = "toml" +version = "0.10.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/be/ba/1f744cdc819428fc6b5084ec34d9b30660f6f9daaf70eead706e3203ec3c/toml-0.10.2.tar.gz", hash = "sha256:b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f", size = 22253, upload-time = "2020-11-01T01:40:22.204Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/44/6f/7120676b6d73228c96e17f1f794d8ab046fc910d781c8d151120c3f1569e/toml-0.10.2-py2.py3-none-any.whl", hash = "sha256:806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b", size = 16588, upload-time = "2020-11-01T01:40:20.672Z" }, +] + [[package]] name = "tomli" version = "2.2.1" @@ -3259,6 +3477,30 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/d0/30/dc54f88dd4a2b5dc8a0279bdd7270e735851848b762aeb1c1184ed1f6b14/tqdm-4.67.1-py3-none-any.whl", hash = "sha256:26445eca388f82e72884e0d580d5464cd801a3ea01e63e5601bdff9ba6a48de2", size = 78540, upload-time = "2024-11-24T20:12:19.698Z" }, ] +[[package]] +name = "typer" +version = "0.24.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "annotated-doc" }, + { name = "click" }, + { name = "rich" }, + { name = "shellingham" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/f5/24/cb09efec5cc954f7f9b930bf8279447d24618bb6758d4f6adf2574c41780/typer-0.24.1.tar.gz", hash = "sha256:e39b4732d65fbdcde189ae76cf7cd48aeae72919dea1fdfc16593be016256b45", size = 118613, upload-time = "2026-02-21T16:54:40.609Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/4a/91/48db081e7a63bb37284f9fbcefda7c44c277b18b0e13fbc36ea2335b71e6/typer-0.24.1-py3-none-any.whl", hash = "sha256:112c1f0ce578bfb4cab9ffdabc68f031416ebcc216536611ba21f04e9aa84c9e", size = 56085, upload-time = "2026-02-21T16:54:41.616Z" }, +] + +[[package]] +name = "types-certifi" +version = "2021.10.8.3" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/52/68/943c3aeaf14624712a0357c4a67814dba5cea36d194f5c764dad7959a00c/types-certifi-2021.10.8.3.tar.gz", hash = "sha256:72cf7798d165bc0b76e1c10dd1ea3097c7063c42c21d664523b928e88b554a4f", size = 2095, upload-time = "2022-06-09T15:19:05.244Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b5/63/2463d89481e811f007b0e1cd0a91e52e141b47f9de724d20db7b861dcfec/types_certifi-2021.10.8.3-py3-none-any.whl", hash = "sha256:b2d1e325e69f71f7c78e5943d410e650b4707bb0ef32e4ddf3da37f54176e88a", size = 2136, upload-time = "2022-06-09T15:19:03.127Z" }, +] + [[package]] name = "types-pynput" version = "1.8.1.20250809" @@ -3280,6 +3522,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/2b/6f/ec0012be842b1d888d46884ac5558fd62aeae1f0ec4f7a581433d890d4b5/types_requests-2.32.4.20250809-py3-none-any.whl", hash = "sha256:f73d1832fb519ece02c85b1f09d5f0dd3108938e7d47e7f94bbfa18a6782b163", size = 20644, upload-time = "2025-08-09T03:17:09.716Z" }, ] +[[package]] +name = "types-toml" +version = "0.10.8.20240310" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/86/47/3e4c75042792bff8e90d7991aa5c51812cc668828cc6cce711e97f63a607/types-toml-0.10.8.20240310.tar.gz", hash = "sha256:3d41501302972436a6b8b239c850b26689657e25281b48ff0ec06345b8830331", size = 4392, upload-time = "2024-03-10T02:18:37.518Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/da/a2/d32ab58c0b216912638b140ab2170ee4b8644067c293b170e19fba340ccc/types_toml-0.10.8.20240310-py3-none-any.whl", hash = "sha256:627b47775d25fa29977d9c70dc0cbab3f314f32c8d8d0c012f2ef5de7aaec05d", size = 4777, upload-time = "2024-03-10T02:18:36.568Z" }, +] + [[package]] name = "typing-extensions" version = "4.14.1" @@ -3365,6 +3616,121 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/33/e8/e40370e6d74ddba47f002a32919d91310d6074130fe4e17dabcafc15cbf1/watchdog-6.0.0-py3-none-win_ia64.whl", hash = "sha256:a1914259fa9e1454315171103c6a30961236f508b9b623eae470268bbcc6a22f", size = 79067, upload-time = "2024-11-01T14:07:11.845Z" }, ] +[[package]] +name = "watchfiles" +version = "1.1.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "anyio" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/c2/c9/8869df9b2a2d6c59d79220a4db37679e74f807c559ffe5265e08b227a210/watchfiles-1.1.1.tar.gz", hash = "sha256:a173cb5c16c4f40ab19cecf48a534c409f7ea983ab8fed0741304a1c0a31b3f2", size = 94440, upload-time = "2025-10-14T15:06:21.08Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a7/1a/206e8cf2dd86fddf939165a57b4df61607a1e0add2785f170a3f616b7d9f/watchfiles-1.1.1-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:eef58232d32daf2ac67f42dea51a2c80f0d03379075d44a587051e63cc2e368c", size = 407318, upload-time = "2025-10-14T15:04:18.753Z" }, + { url = "https://files.pythonhosted.org/packages/b3/0f/abaf5262b9c496b5dad4ed3c0e799cbecb1f8ea512ecb6ddd46646a9fca3/watchfiles-1.1.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:03fa0f5237118a0c5e496185cafa92878568b652a2e9a9382a5151b1a0380a43", size = 394478, upload-time = "2025-10-14T15:04:20.297Z" }, + { url = "https://files.pythonhosted.org/packages/b1/04/9cc0ba88697b34b755371f5ace8d3a4d9a15719c07bdc7bd13d7d8c6a341/watchfiles-1.1.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8ca65483439f9c791897f7db49202301deb6e15fe9f8fe2fed555bf986d10c31", size = 449894, upload-time = "2025-10-14T15:04:21.527Z" }, + { url = "https://files.pythonhosted.org/packages/d2/9c/eda4615863cd8621e89aed4df680d8c3ec3da6a4cf1da113c17decd87c7f/watchfiles-1.1.1-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:f0ab1c1af0cb38e3f598244c17919fb1a84d1629cc08355b0074b6d7f53138ac", size = 459065, upload-time = "2025-10-14T15:04:22.795Z" }, + { url = "https://files.pythonhosted.org/packages/84/13/f28b3f340157d03cbc8197629bc109d1098764abe1e60874622a0be5c112/watchfiles-1.1.1-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3bc570d6c01c206c46deb6e935a260be44f186a2f05179f52f7fcd2be086a94d", size = 488377, upload-time = "2025-10-14T15:04:24.138Z" }, + { url = "https://files.pythonhosted.org/packages/86/93/cfa597fa9389e122488f7ffdbd6db505b3b915ca7435ecd7542e855898c2/watchfiles-1.1.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e84087b432b6ac94778de547e08611266f1f8ffad28c0ee4c82e028b0fc5966d", size = 595837, upload-time = "2025-10-14T15:04:25.057Z" }, + { url = "https://files.pythonhosted.org/packages/57/1e/68c1ed5652b48d89fc24d6af905d88ee4f82fa8bc491e2666004e307ded1/watchfiles-1.1.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:620bae625f4cb18427b1bb1a2d9426dc0dd5a5ba74c7c2cdb9de405f7b129863", size = 473456, upload-time = "2025-10-14T15:04:26.497Z" }, + { url = "https://files.pythonhosted.org/packages/d5/dc/1a680b7458ffa3b14bb64878112aefc8f2e4f73c5af763cbf0bd43100658/watchfiles-1.1.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:544364b2b51a9b0c7000a4b4b02f90e9423d97fbbf7e06689236443ebcad81ab", size = 455614, upload-time = "2025-10-14T15:04:27.539Z" }, + { url = "https://files.pythonhosted.org/packages/61/a5/3d782a666512e01eaa6541a72ebac1d3aae191ff4a31274a66b8dd85760c/watchfiles-1.1.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:bbe1ef33d45bc71cf21364df962af171f96ecaeca06bd9e3d0b583efb12aec82", size = 630690, upload-time = "2025-10-14T15:04:28.495Z" }, + { url = "https://files.pythonhosted.org/packages/9b/73/bb5f38590e34687b2a9c47a244aa4dd50c56a825969c92c9c5fc7387cea1/watchfiles-1.1.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:1a0bb430adb19ef49389e1ad368450193a90038b5b752f4ac089ec6942c4dff4", size = 622459, upload-time = "2025-10-14T15:04:29.491Z" }, + { url = "https://files.pythonhosted.org/packages/f1/ac/c9bb0ec696e07a20bd58af5399aeadaef195fb2c73d26baf55180fe4a942/watchfiles-1.1.1-cp310-cp310-win32.whl", hash = "sha256:3f6d37644155fb5beca5378feb8c1708d5783145f2a0f1c4d5a061a210254844", size = 272663, upload-time = "2025-10-14T15:04:30.435Z" }, + { url = "https://files.pythonhosted.org/packages/11/a0/a60c5a7c2ec59fa062d9a9c61d02e3b6abd94d32aac2d8344c4bdd033326/watchfiles-1.1.1-cp310-cp310-win_amd64.whl", hash = "sha256:a36d8efe0f290835fd0f33da35042a1bb5dc0e83cbc092dcf69bce442579e88e", size = 287453, upload-time = "2025-10-14T15:04:31.53Z" }, + { url = "https://files.pythonhosted.org/packages/1f/f8/2c5f479fb531ce2f0564eda479faecf253d886b1ab3630a39b7bf7362d46/watchfiles-1.1.1-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:f57b396167a2565a4e8b5e56a5a1c537571733992b226f4f1197d79e94cf0ae5", size = 406529, upload-time = "2025-10-14T15:04:32.899Z" }, + { url = "https://files.pythonhosted.org/packages/fe/cd/f515660b1f32f65df671ddf6f85bfaca621aee177712874dc30a97397977/watchfiles-1.1.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:421e29339983e1bebc281fab40d812742268ad057db4aee8c4d2bce0af43b741", size = 394384, upload-time = "2025-10-14T15:04:33.761Z" }, + { url = "https://files.pythonhosted.org/packages/7b/c3/28b7dc99733eab43fca2d10f55c86e03bd6ab11ca31b802abac26b23d161/watchfiles-1.1.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6e43d39a741e972bab5d8100b5cdacf69db64e34eb19b6e9af162bccf63c5cc6", size = 448789, upload-time = "2025-10-14T15:04:34.679Z" }, + { url = "https://files.pythonhosted.org/packages/4a/24/33e71113b320030011c8e4316ccca04194bf0cbbaeee207f00cbc7d6b9f5/watchfiles-1.1.1-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:f537afb3276d12814082a2e9b242bdcf416c2e8fd9f799a737990a1dbe906e5b", size = 460521, upload-time = "2025-10-14T15:04:35.963Z" }, + { url = "https://files.pythonhosted.org/packages/f4/c3/3c9a55f255aa57b91579ae9e98c88704955fa9dac3e5614fb378291155df/watchfiles-1.1.1-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b2cd9e04277e756a2e2d2543d65d1e2166d6fd4c9b183f8808634fda23f17b14", size = 488722, upload-time = "2025-10-14T15:04:37.091Z" }, + { url = "https://files.pythonhosted.org/packages/49/36/506447b73eb46c120169dc1717fe2eff07c234bb3232a7200b5f5bd816e9/watchfiles-1.1.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5f3f58818dc0b07f7d9aa7fe9eb1037aecb9700e63e1f6acfed13e9fef648f5d", size = 596088, upload-time = "2025-10-14T15:04:38.39Z" }, + { url = "https://files.pythonhosted.org/packages/82/ab/5f39e752a9838ec4d52e9b87c1e80f1ee3ccdbe92e183c15b6577ab9de16/watchfiles-1.1.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9bb9f66367023ae783551042d31b1d7fd422e8289eedd91f26754a66f44d5cff", size = 472923, upload-time = "2025-10-14T15:04:39.666Z" }, + { url = "https://files.pythonhosted.org/packages/af/b9/a419292f05e302dea372fa7e6fda5178a92998411f8581b9830d28fb9edb/watchfiles-1.1.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:aebfd0861a83e6c3d1110b78ad54704486555246e542be3e2bb94195eabb2606", size = 456080, upload-time = "2025-10-14T15:04:40.643Z" }, + { url = "https://files.pythonhosted.org/packages/b0/c3/d5932fd62bde1a30c36e10c409dc5d54506726f08cb3e1d8d0ba5e2bc8db/watchfiles-1.1.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:5fac835b4ab3c6487b5dbad78c4b3724e26bcc468e886f8ba8cc4306f68f6701", size = 629432, upload-time = "2025-10-14T15:04:41.789Z" }, + { url = "https://files.pythonhosted.org/packages/f7/77/16bddd9779fafb795f1a94319dc965209c5641db5bf1edbbccace6d1b3c0/watchfiles-1.1.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:399600947b170270e80134ac854e21b3ccdefa11a9529a3decc1327088180f10", size = 623046, upload-time = "2025-10-14T15:04:42.718Z" }, + { url = "https://files.pythonhosted.org/packages/46/ef/f2ecb9a0f342b4bfad13a2787155c6ee7ce792140eac63a34676a2feeef2/watchfiles-1.1.1-cp311-cp311-win32.whl", hash = "sha256:de6da501c883f58ad50db3a32ad397b09ad29865b5f26f64c24d3e3281685849", size = 271473, upload-time = "2025-10-14T15:04:43.624Z" }, + { url = "https://files.pythonhosted.org/packages/94/bc/f42d71125f19731ea435c3948cad148d31a64fccde3867e5ba4edee901f9/watchfiles-1.1.1-cp311-cp311-win_amd64.whl", hash = "sha256:35c53bd62a0b885bf653ebf6b700d1bf05debb78ad9292cf2a942b23513dc4c4", size = 287598, upload-time = "2025-10-14T15:04:44.516Z" }, + { url = "https://files.pythonhosted.org/packages/57/c9/a30f897351f95bbbfb6abcadafbaca711ce1162f4db95fc908c98a9165f3/watchfiles-1.1.1-cp311-cp311-win_arm64.whl", hash = "sha256:57ca5281a8b5e27593cb7d82c2ac927ad88a96ed406aa446f6344e4328208e9e", size = 277210, upload-time = "2025-10-14T15:04:45.883Z" }, + { url = "https://files.pythonhosted.org/packages/74/d5/f039e7e3c639d9b1d09b07ea412a6806d38123f0508e5f9b48a87b0a76cc/watchfiles-1.1.1-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:8c89f9f2f740a6b7dcc753140dd5e1ab9215966f7a3530d0c0705c83b401bd7d", size = 404745, upload-time = "2025-10-14T15:04:46.731Z" }, + { url = "https://files.pythonhosted.org/packages/a5/96/a881a13aa1349827490dab2d363c8039527060cfcc2c92cc6d13d1b1049e/watchfiles-1.1.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:bd404be08018c37350f0d6e34676bd1e2889990117a2b90070b3007f172d0610", size = 391769, upload-time = "2025-10-14T15:04:48.003Z" }, + { url = "https://files.pythonhosted.org/packages/4b/5b/d3b460364aeb8da471c1989238ea0e56bec24b6042a68046adf3d9ddb01c/watchfiles-1.1.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8526e8f916bb5b9a0a777c8317c23ce65de259422bba5b31325a6fa6029d33af", size = 449374, upload-time = "2025-10-14T15:04:49.179Z" }, + { url = "https://files.pythonhosted.org/packages/b9/44/5769cb62d4ed055cb17417c0a109a92f007114a4e07f30812a73a4efdb11/watchfiles-1.1.1-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:2edc3553362b1c38d9f06242416a5d8e9fe235c204a4072e988ce2e5bb1f69f6", size = 459485, upload-time = "2025-10-14T15:04:50.155Z" }, + { url = "https://files.pythonhosted.org/packages/19/0c/286b6301ded2eccd4ffd0041a1b726afda999926cf720aab63adb68a1e36/watchfiles-1.1.1-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:30f7da3fb3f2844259cba4720c3fc7138eb0f7b659c38f3bfa65084c7fc7abce", size = 488813, upload-time = "2025-10-14T15:04:51.059Z" }, + { url = "https://files.pythonhosted.org/packages/c7/2b/8530ed41112dd4a22f4dcfdb5ccf6a1baad1ff6eed8dc5a5f09e7e8c41c7/watchfiles-1.1.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f8979280bdafff686ba5e4d8f97840f929a87ed9cdf133cbbd42f7766774d2aa", size = 594816, upload-time = "2025-10-14T15:04:52.031Z" }, + { url = "https://files.pythonhosted.org/packages/ce/d2/f5f9fb49489f184f18470d4f99f4e862a4b3e9ac2865688eb2099e3d837a/watchfiles-1.1.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:dcc5c24523771db3a294c77d94771abcfcb82a0e0ee8efd910c37c59ec1b31bb", size = 475186, upload-time = "2025-10-14T15:04:53.064Z" }, + { url = "https://files.pythonhosted.org/packages/cf/68/5707da262a119fb06fbe214d82dd1fe4a6f4af32d2d14de368d0349eb52a/watchfiles-1.1.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1db5d7ae38ff20153d542460752ff397fcf5c96090c1230803713cf3147a6803", size = 456812, upload-time = "2025-10-14T15:04:55.174Z" }, + { url = "https://files.pythonhosted.org/packages/66/ab/3cbb8756323e8f9b6f9acb9ef4ec26d42b2109bce830cc1f3468df20511d/watchfiles-1.1.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:28475ddbde92df1874b6c5c8aaeb24ad5be47a11f87cde5a28ef3835932e3e94", size = 630196, upload-time = "2025-10-14T15:04:56.22Z" }, + { url = "https://files.pythonhosted.org/packages/78/46/7152ec29b8335f80167928944a94955015a345440f524d2dfe63fc2f437b/watchfiles-1.1.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:36193ed342f5b9842edd3532729a2ad55c4160ffcfa3700e0d54be496b70dd43", size = 622657, upload-time = "2025-10-14T15:04:57.521Z" }, + { url = "https://files.pythonhosted.org/packages/0a/bf/95895e78dd75efe9a7f31733607f384b42eb5feb54bd2eb6ed57cc2e94f4/watchfiles-1.1.1-cp312-cp312-win32.whl", hash = "sha256:859e43a1951717cc8de7f4c77674a6d389b106361585951d9e69572823f311d9", size = 272042, upload-time = "2025-10-14T15:04:59.046Z" }, + { url = "https://files.pythonhosted.org/packages/87/0a/90eb755f568de2688cb220171c4191df932232c20946966c27a59c400850/watchfiles-1.1.1-cp312-cp312-win_amd64.whl", hash = "sha256:91d4c9a823a8c987cce8fa2690923b069966dabb196dd8d137ea2cede885fde9", size = 288410, upload-time = "2025-10-14T15:05:00.081Z" }, + { url = "https://files.pythonhosted.org/packages/36/76/f322701530586922fbd6723c4f91ace21364924822a8772c549483abed13/watchfiles-1.1.1-cp312-cp312-win_arm64.whl", hash = "sha256:a625815d4a2bdca61953dbba5a39d60164451ef34c88d751f6c368c3ea73d404", size = 278209, upload-time = "2025-10-14T15:05:01.168Z" }, + { url = "https://files.pythonhosted.org/packages/bb/f4/f750b29225fe77139f7ae5de89d4949f5a99f934c65a1f1c0b248f26f747/watchfiles-1.1.1-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:130e4876309e8686a5e37dba7d5e9bc77e6ed908266996ca26572437a5271e18", size = 404321, upload-time = "2025-10-14T15:05:02.063Z" }, + { url = "https://files.pythonhosted.org/packages/2b/f9/f07a295cde762644aa4c4bb0f88921d2d141af45e735b965fb2e87858328/watchfiles-1.1.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:5f3bde70f157f84ece3765b42b4a52c6ac1a50334903c6eaf765362f6ccca88a", size = 391783, upload-time = "2025-10-14T15:05:03.052Z" }, + { url = "https://files.pythonhosted.org/packages/bc/11/fc2502457e0bea39a5c958d86d2cb69e407a4d00b85735ca724bfa6e0d1a/watchfiles-1.1.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:14e0b1fe858430fc0251737ef3824c54027bedb8c37c38114488b8e131cf8219", size = 449279, upload-time = "2025-10-14T15:05:04.004Z" }, + { url = "https://files.pythonhosted.org/packages/e3/1f/d66bc15ea0b728df3ed96a539c777acfcad0eb78555ad9efcaa1274688f0/watchfiles-1.1.1-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:f27db948078f3823a6bb3b465180db8ebecf26dd5dae6f6180bd87383b6b4428", size = 459405, upload-time = "2025-10-14T15:05:04.942Z" }, + { url = "https://files.pythonhosted.org/packages/be/90/9f4a65c0aec3ccf032703e6db02d89a157462fbb2cf20dd415128251cac0/watchfiles-1.1.1-cp313-cp313-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:059098c3a429f62fc98e8ec62b982230ef2c8df68c79e826e37b895bc359a9c0", size = 488976, upload-time = "2025-10-14T15:05:05.905Z" }, + { url = "https://files.pythonhosted.org/packages/37/57/ee347af605d867f712be7029bb94c8c071732a4b44792e3176fa3c612d39/watchfiles-1.1.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:bfb5862016acc9b869bb57284e6cb35fdf8e22fe59f7548858e2f971d045f150", size = 595506, upload-time = "2025-10-14T15:05:06.906Z" }, + { url = "https://files.pythonhosted.org/packages/a8/78/cc5ab0b86c122047f75e8fc471c67a04dee395daf847d3e59381996c8707/watchfiles-1.1.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:319b27255aacd9923b8a276bb14d21a5f7ff82564c744235fc5eae58d95422ae", size = 474936, upload-time = "2025-10-14T15:05:07.906Z" }, + { url = "https://files.pythonhosted.org/packages/62/da/def65b170a3815af7bd40a3e7010bf6ab53089ef1b75d05dd5385b87cf08/watchfiles-1.1.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c755367e51db90e75b19454b680903631d41f9e3607fbd941d296a020c2d752d", size = 456147, upload-time = "2025-10-14T15:05:09.138Z" }, + { url = "https://files.pythonhosted.org/packages/57/99/da6573ba71166e82d288d4df0839128004c67d2778d3b566c138695f5c0b/watchfiles-1.1.1-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:c22c776292a23bfc7237a98f791b9ad3144b02116ff10d820829ce62dff46d0b", size = 630007, upload-time = "2025-10-14T15:05:10.117Z" }, + { url = "https://files.pythonhosted.org/packages/a8/51/7439c4dd39511368849eb1e53279cd3454b4a4dbace80bab88feeb83c6b5/watchfiles-1.1.1-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:3a476189be23c3686bc2f4321dd501cb329c0a0469e77b7b534ee10129ae6374", size = 622280, upload-time = "2025-10-14T15:05:11.146Z" }, + { url = "https://files.pythonhosted.org/packages/95/9c/8ed97d4bba5db6fdcdb2b298d3898f2dd5c20f6b73aee04eabe56c59677e/watchfiles-1.1.1-cp313-cp313-win32.whl", hash = "sha256:bf0a91bfb5574a2f7fc223cf95eeea79abfefa404bf1ea5e339c0c1560ae99a0", size = 272056, upload-time = "2025-10-14T15:05:12.156Z" }, + { url = "https://files.pythonhosted.org/packages/1f/f3/c14e28429f744a260d8ceae18bf58c1d5fa56b50d006a7a9f80e1882cb0d/watchfiles-1.1.1-cp313-cp313-win_amd64.whl", hash = "sha256:52e06553899e11e8074503c8e716d574adeeb7e68913115c4b3653c53f9bae42", size = 288162, upload-time = "2025-10-14T15:05:13.208Z" }, + { url = "https://files.pythonhosted.org/packages/dc/61/fe0e56c40d5cd29523e398d31153218718c5786b5e636d9ae8ae79453d27/watchfiles-1.1.1-cp313-cp313-win_arm64.whl", hash = "sha256:ac3cc5759570cd02662b15fbcd9d917f7ecd47efe0d6b40474eafd246f91ea18", size = 277909, upload-time = "2025-10-14T15:05:14.49Z" }, + { url = "https://files.pythonhosted.org/packages/79/42/e0a7d749626f1e28c7108a99fb9bf524b501bbbeb9b261ceecde644d5a07/watchfiles-1.1.1-cp313-cp313t-macosx_10_12_x86_64.whl", hash = "sha256:563b116874a9a7ce6f96f87cd0b94f7faf92d08d0021e837796f0a14318ef8da", size = 403389, upload-time = "2025-10-14T15:05:15.777Z" }, + { url = "https://files.pythonhosted.org/packages/15/49/08732f90ce0fbbc13913f9f215c689cfc9ced345fb1bcd8829a50007cc8d/watchfiles-1.1.1-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:3ad9fe1dae4ab4212d8c91e80b832425e24f421703b5a42ef2e4a1e215aff051", size = 389964, upload-time = "2025-10-14T15:05:16.85Z" }, + { url = "https://files.pythonhosted.org/packages/27/0d/7c315d4bd5f2538910491a0393c56bf70d333d51bc5b34bee8e68e8cea19/watchfiles-1.1.1-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ce70f96a46b894b36eba678f153f052967a0d06d5b5a19b336ab0dbbd029f73e", size = 448114, upload-time = "2025-10-14T15:05:17.876Z" }, + { url = "https://files.pythonhosted.org/packages/c3/24/9e096de47a4d11bc4df41e9d1e61776393eac4cb6eb11b3e23315b78b2cc/watchfiles-1.1.1-cp313-cp313t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:cb467c999c2eff23a6417e58d75e5828716f42ed8289fe6b77a7e5a91036ca70", size = 460264, upload-time = "2025-10-14T15:05:18.962Z" }, + { url = "https://files.pythonhosted.org/packages/cc/0f/e8dea6375f1d3ba5fcb0b3583e2b493e77379834c74fd5a22d66d85d6540/watchfiles-1.1.1-cp313-cp313t-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:836398932192dae4146c8f6f737d74baeac8b70ce14831a239bdb1ca882fc261", size = 487877, upload-time = "2025-10-14T15:05:20.094Z" }, + { url = "https://files.pythonhosted.org/packages/ac/5b/df24cfc6424a12deb41503b64d42fbea6b8cb357ec62ca84a5a3476f654a/watchfiles-1.1.1-cp313-cp313t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:743185e7372b7bc7c389e1badcc606931a827112fbbd37f14c537320fca08620", size = 595176, upload-time = "2025-10-14T15:05:21.134Z" }, + { url = "https://files.pythonhosted.org/packages/8f/b5/853b6757f7347de4e9b37e8cc3289283fb983cba1ab4d2d7144694871d9c/watchfiles-1.1.1-cp313-cp313t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:afaeff7696e0ad9f02cbb8f56365ff4686ab205fcf9c4c5b6fdfaaa16549dd04", size = 473577, upload-time = "2025-10-14T15:05:22.306Z" }, + { url = "https://files.pythonhosted.org/packages/e1/f7/0a4467be0a56e80447c8529c9fce5b38eab4f513cb3d9bf82e7392a5696b/watchfiles-1.1.1-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3f7eb7da0eb23aa2ba036d4f616d46906013a68caf61b7fdbe42fc8b25132e77", size = 455425, upload-time = "2025-10-14T15:05:23.348Z" }, + { url = "https://files.pythonhosted.org/packages/8e/e0/82583485ea00137ddf69bc84a2db88bd92ab4a6e3c405e5fb878ead8d0e7/watchfiles-1.1.1-cp313-cp313t-musllinux_1_1_aarch64.whl", hash = "sha256:831a62658609f0e5c64178211c942ace999517f5770fe9436be4c2faeba0c0ef", size = 628826, upload-time = "2025-10-14T15:05:24.398Z" }, + { url = "https://files.pythonhosted.org/packages/28/9a/a785356fccf9fae84c0cc90570f11702ae9571036fb25932f1242c82191c/watchfiles-1.1.1-cp313-cp313t-musllinux_1_1_x86_64.whl", hash = "sha256:f9a2ae5c91cecc9edd47e041a930490c31c3afb1f5e6d71de3dc671bfaca02bf", size = 622208, upload-time = "2025-10-14T15:05:25.45Z" }, + { url = "https://files.pythonhosted.org/packages/c3/f4/0872229324ef69b2c3edec35e84bd57a1289e7d3fe74588048ed8947a323/watchfiles-1.1.1-cp314-cp314-macosx_10_12_x86_64.whl", hash = "sha256:d1715143123baeeaeadec0528bb7441103979a1d5f6fd0e1f915383fea7ea6d5", size = 404315, upload-time = "2025-10-14T15:05:26.501Z" }, + { url = "https://files.pythonhosted.org/packages/7b/22/16d5331eaed1cb107b873f6ae1b69e9ced582fcf0c59a50cd84f403b1c32/watchfiles-1.1.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:39574d6370c4579d7f5d0ad940ce5b20db0e4117444e39b6d8f99db5676c52fd", size = 390869, upload-time = "2025-10-14T15:05:27.649Z" }, + { url = "https://files.pythonhosted.org/packages/b2/7e/5643bfff5acb6539b18483128fdc0ef2cccc94a5b8fbda130c823e8ed636/watchfiles-1.1.1-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7365b92c2e69ee952902e8f70f3ba6360d0d596d9299d55d7d386df84b6941fb", size = 449919, upload-time = "2025-10-14T15:05:28.701Z" }, + { url = "https://files.pythonhosted.org/packages/51/2e/c410993ba5025a9f9357c376f48976ef0e1b1aefb73b97a5ae01a5972755/watchfiles-1.1.1-cp314-cp314-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:bfff9740c69c0e4ed32416f013f3c45e2ae42ccedd1167ef2d805c000b6c71a5", size = 460845, upload-time = "2025-10-14T15:05:30.064Z" }, + { url = "https://files.pythonhosted.org/packages/8e/a4/2df3b404469122e8680f0fcd06079317e48db58a2da2950fb45020947734/watchfiles-1.1.1-cp314-cp314-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b27cf2eb1dda37b2089e3907d8ea92922b673c0c427886d4edc6b94d8dfe5db3", size = 489027, upload-time = "2025-10-14T15:05:31.064Z" }, + { url = "https://files.pythonhosted.org/packages/ea/84/4587ba5b1f267167ee715b7f66e6382cca6938e0a4b870adad93e44747e6/watchfiles-1.1.1-cp314-cp314-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:526e86aced14a65a5b0ec50827c745597c782ff46b571dbfe46192ab9e0b3c33", size = 595615, upload-time = "2025-10-14T15:05:32.074Z" }, + { url = "https://files.pythonhosted.org/packages/6a/0f/c6988c91d06e93cd0bb3d4a808bcf32375ca1904609835c3031799e3ecae/watchfiles-1.1.1-cp314-cp314-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:04e78dd0b6352db95507fd8cb46f39d185cf8c74e4cf1e4fbad1d3df96faf510", size = 474836, upload-time = "2025-10-14T15:05:33.209Z" }, + { url = "https://files.pythonhosted.org/packages/b4/36/ded8aebea91919485b7bbabbd14f5f359326cb5ec218cd67074d1e426d74/watchfiles-1.1.1-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5c85794a4cfa094714fb9c08d4a218375b2b95b8ed1666e8677c349906246c05", size = 455099, upload-time = "2025-10-14T15:05:34.189Z" }, + { url = "https://files.pythonhosted.org/packages/98/e0/8c9bdba88af756a2fce230dd365fab2baf927ba42cd47521ee7498fd5211/watchfiles-1.1.1-cp314-cp314-musllinux_1_1_aarch64.whl", hash = "sha256:74d5012b7630714b66be7b7b7a78855ef7ad58e8650c73afc4c076a1f480a8d6", size = 630626, upload-time = "2025-10-14T15:05:35.216Z" }, + { url = "https://files.pythonhosted.org/packages/2a/84/a95db05354bf2d19e438520d92a8ca475e578c647f78f53197f5a2f17aaf/watchfiles-1.1.1-cp314-cp314-musllinux_1_1_x86_64.whl", hash = "sha256:8fbe85cb3201c7d380d3d0b90e63d520f15d6afe217165d7f98c9c649654db81", size = 622519, upload-time = "2025-10-14T15:05:36.259Z" }, + { url = "https://files.pythonhosted.org/packages/1d/ce/d8acdc8de545de995c339be67711e474c77d643555a9bb74a9334252bd55/watchfiles-1.1.1-cp314-cp314-win32.whl", hash = "sha256:3fa0b59c92278b5a7800d3ee7733da9d096d4aabcfabb9a928918bd276ef9b9b", size = 272078, upload-time = "2025-10-14T15:05:37.63Z" }, + { url = "https://files.pythonhosted.org/packages/c4/c9/a74487f72d0451524be827e8edec251da0cc1fcf111646a511ae752e1a3d/watchfiles-1.1.1-cp314-cp314-win_amd64.whl", hash = "sha256:c2047d0b6cea13b3316bdbafbfa0c4228ae593d995030fda39089d36e64fc03a", size = 287664, upload-time = "2025-10-14T15:05:38.95Z" }, + { url = "https://files.pythonhosted.org/packages/df/b8/8ac000702cdd496cdce998c6f4ee0ca1f15977bba51bdf07d872ebdfc34c/watchfiles-1.1.1-cp314-cp314-win_arm64.whl", hash = "sha256:842178b126593addc05acf6fce960d28bc5fae7afbaa2c6c1b3a7b9460e5be02", size = 277154, upload-time = "2025-10-14T15:05:39.954Z" }, + { url = "https://files.pythonhosted.org/packages/47/a8/e3af2184707c29f0f14b1963c0aace6529f9d1b8582d5b99f31bbf42f59e/watchfiles-1.1.1-cp314-cp314t-macosx_10_12_x86_64.whl", hash = "sha256:88863fbbc1a7312972f1c511f202eb30866370ebb8493aef2812b9ff28156a21", size = 403820, upload-time = "2025-10-14T15:05:40.932Z" }, + { url = "https://files.pythonhosted.org/packages/c0/ec/e47e307c2f4bd75f9f9e8afbe3876679b18e1bcec449beca132a1c5ffb2d/watchfiles-1.1.1-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:55c7475190662e202c08c6c0f4d9e345a29367438cf8e8037f3155e10a88d5a5", size = 390510, upload-time = "2025-10-14T15:05:41.945Z" }, + { url = "https://files.pythonhosted.org/packages/d5/a0/ad235642118090f66e7b2f18fd5c42082418404a79205cdfca50b6309c13/watchfiles-1.1.1-cp314-cp314t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3f53fa183d53a1d7a8852277c92b967ae99c2d4dcee2bfacff8868e6e30b15f7", size = 448408, upload-time = "2025-10-14T15:05:43.385Z" }, + { url = "https://files.pythonhosted.org/packages/df/85/97fa10fd5ff3332ae17e7e40e20784e419e28521549780869f1413742e9d/watchfiles-1.1.1-cp314-cp314t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:6aae418a8b323732fa89721d86f39ec8f092fc2af67f4217a2b07fd3e93c6101", size = 458968, upload-time = "2025-10-14T15:05:44.404Z" }, + { url = "https://files.pythonhosted.org/packages/47/c2/9059c2e8966ea5ce678166617a7f75ecba6164375f3b288e50a40dc6d489/watchfiles-1.1.1-cp314-cp314t-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f096076119da54a6080e8920cbdaac3dbee667eb91dcc5e5b78840b87415bd44", size = 488096, upload-time = "2025-10-14T15:05:45.398Z" }, + { url = "https://files.pythonhosted.org/packages/94/44/d90a9ec8ac309bc26db808a13e7bfc0e4e78b6fc051078a554e132e80160/watchfiles-1.1.1-cp314-cp314t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:00485f441d183717038ed2e887a7c868154f216877653121068107b227a2f64c", size = 596040, upload-time = "2025-10-14T15:05:46.502Z" }, + { url = "https://files.pythonhosted.org/packages/95/68/4e3479b20ca305cfc561db3ed207a8a1c745ee32bf24f2026a129d0ddb6e/watchfiles-1.1.1-cp314-cp314t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a55f3e9e493158d7bfdb60a1165035f1cf7d320914e7b7ea83fe22c6023b58fc", size = 473847, upload-time = "2025-10-14T15:05:47.484Z" }, + { url = "https://files.pythonhosted.org/packages/4f/55/2af26693fd15165c4ff7857e38330e1b61ab8c37d15dc79118cdba115b7a/watchfiles-1.1.1-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8c91ed27800188c2ae96d16e3149f199d62f86c7af5f5f4d2c61a3ed8cd3666c", size = 455072, upload-time = "2025-10-14T15:05:48.928Z" }, + { url = "https://files.pythonhosted.org/packages/66/1d/d0d200b10c9311ec25d2273f8aad8c3ef7cc7ea11808022501811208a750/watchfiles-1.1.1-cp314-cp314t-musllinux_1_1_aarch64.whl", hash = "sha256:311ff15a0bae3714ffb603e6ba6dbfba4065ab60865d15a6ec544133bdb21099", size = 629104, upload-time = "2025-10-14T15:05:49.908Z" }, + { url = "https://files.pythonhosted.org/packages/e3/bd/fa9bb053192491b3867ba07d2343d9f2252e00811567d30ae8d0f78136fe/watchfiles-1.1.1-cp314-cp314t-musllinux_1_1_x86_64.whl", hash = "sha256:a916a2932da8f8ab582f242c065f5c81bed3462849ca79ee357dd9551b0e9b01", size = 622112, upload-time = "2025-10-14T15:05:50.941Z" }, + { url = "https://files.pythonhosted.org/packages/ba/4c/a888c91e2e326872fa4705095d64acd8aa2fb9c1f7b9bd0588f33850516c/watchfiles-1.1.1-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:17ef139237dfced9da49fb7f2232c86ca9421f666d78c264c7ffca6601d154c3", size = 409611, upload-time = "2025-10-14T15:06:05.809Z" }, + { url = "https://files.pythonhosted.org/packages/1e/c7/5420d1943c8e3ce1a21c0a9330bcf7edafb6aa65d26b21dbb3267c9e8112/watchfiles-1.1.1-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:672b8adf25b1a0d35c96b5888b7b18699d27d4194bac8beeae75be4b7a3fc9b2", size = 396889, upload-time = "2025-10-14T15:06:07.035Z" }, + { url = "https://files.pythonhosted.org/packages/0c/e5/0072cef3804ce8d3aaddbfe7788aadff6b3d3f98a286fdbee9fd74ca59a7/watchfiles-1.1.1-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:77a13aea58bc2b90173bc69f2a90de8e282648939a00a602e1dc4ee23e26b66d", size = 451616, upload-time = "2025-10-14T15:06:08.072Z" }, + { url = "https://files.pythonhosted.org/packages/83/4e/b87b71cbdfad81ad7e83358b3e447fedd281b880a03d64a760fe0a11fc2e/watchfiles-1.1.1-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0b495de0bb386df6a12b18335a0285dda90260f51bdb505503c02bcd1ce27a8b", size = 458413, upload-time = "2025-10-14T15:06:09.209Z" }, + { url = "https://files.pythonhosted.org/packages/d3/8e/e500f8b0b77be4ff753ac94dc06b33d8f0d839377fee1b78e8c8d8f031bf/watchfiles-1.1.1-pp311-pypy311_pp73-macosx_10_12_x86_64.whl", hash = "sha256:db476ab59b6765134de1d4fe96a1a9c96ddf091683599be0f26147ea1b2e4b88", size = 408250, upload-time = "2025-10-14T15:06:10.264Z" }, + { url = "https://files.pythonhosted.org/packages/bd/95/615e72cd27b85b61eec764a5ca51bd94d40b5adea5ff47567d9ebc4d275a/watchfiles-1.1.1-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:89eef07eee5e9d1fda06e38822ad167a044153457e6fd997f8a858ab7564a336", size = 396117, upload-time = "2025-10-14T15:06:11.28Z" }, + { url = "https://files.pythonhosted.org/packages/c9/81/e7fe958ce8a7fb5c73cc9fb07f5aeaf755e6aa72498c57d760af760c91f8/watchfiles-1.1.1-pp311-pypy311_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ce19e06cbda693e9e7686358af9cd6f5d61312ab8b00488bc36f5aabbaf77e24", size = 450493, upload-time = "2025-10-14T15:06:12.321Z" }, + { url = "https://files.pythonhosted.org/packages/6e/d4/ed38dd3b1767193de971e694aa544356e63353c33a85d948166b5ff58b9e/watchfiles-1.1.1-pp311-pypy311_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3e6f39af2eab0118338902798b5aa6664f46ff66bc0280de76fca67a7f262a49", size = 457546, upload-time = "2025-10-14T15:06:13.372Z" }, +] + +[[package]] +name = "wcmatch" +version = "10.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "bracex" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/79/3e/c0bdc27cf06f4e47680bd5803a07cb3dfd17de84cde92dd217dcb9e05253/wcmatch-10.1.tar.gz", hash = "sha256:f11f94208c8c8484a16f4f48638a85d771d9513f4ab3f37595978801cb9465af", size = 117421, upload-time = "2025-06-22T19:14:02.49Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/eb/d8/0d1d2e9d3fabcf5d6840362adcf05f8cf3cd06a73358140c3a97189238ae/wcmatch-10.1-py3-none-any.whl", hash = "sha256:5848ace7dbb0476e5e55ab63c6bbd529745089343427caa5537f230cc01beb8a", size = 39854, upload-time = "2025-06-22T19:14:00.978Z" }, +] + [[package]] name = "websockets" version = "15.0.1" From b307543afefec0d77005d54874668ae2dfece0c7 Mon Sep 17 00:00:00 2001 From: Kazuhiro Sera Date: Sun, 15 Mar 2026 10:07:46 -0700 Subject: [PATCH 02/11] fix: align ephemeral mount persistence across sandbox backends (#10) This pull request fixes a shared sandbox persistence contract bug across Docker, E2B, Modal, and Unix-local. Before this change, Docker had branch-local logic to account for actual in-workspace mount targets, but the other backends still reasoned mostly from the logical manifest key. A manifest such as `entries={"logical": Mount(..., mount_path=Path("actual"))}` could therefore exclude `logical` while still persisting or snapshotting `actual` in Unix-local, Modal, and parts of E2B. E2B also enumerated mounts only from top-level manifest entries, so nested mounts under `Dir(...)` were never unmounted before snapshotting. The fix moves that contract into shared manifest and mount helpers. The manifest now computes effective ephemeral persistence paths from both logical entry paths and resolved in-workspace mount targets, and it exposes deepest-first ephemeral mount targets for backends that need temporary unmount/remount around persistence. Docker is refactored onto those shared helpers, Unix-local and Modal now exclude relocated mount targets consistently, and E2B now handles nested mounts plus rollback-safe remount behavior when persistence setup fails partway through. The regression coverage locks the contract at both the shared-helper and backend levels. The new tests cover relocated `mount_path` exclusion, nested mount traversal and ordering, E2B unmount/remount rollback, and Modal persistence modes so these backends do not drift apart again. --- .../extensions/sandbox/sandboxes/e2b.py | 71 ++-- .../extensions/sandbox/sandboxes/modal.py | 6 +- src/agents/sandbox/entries/mounts/base.py | 7 + src/agents/sandbox/manifest.py | 62 +++- src/agents/sandbox/sandboxes/docker.py | 137 +++++-- src/agents/sandbox/sandboxes/unix_local.py | 2 +- tests/extensions/test_sandbox_e2b.py | 149 +++++++- tests/extensions/test_sandbox_modal.py | 198 ++++++++++- tests/test_sandbox_docker.py | 335 +++++++++++++++++- tests/test_sandbox_extract.py | 28 ++ tests/test_sandbox_manifest.py | 65 ++++ 11 files changed, 983 insertions(+), 77 deletions(-) diff --git a/src/agents/extensions/sandbox/sandboxes/e2b.py b/src/agents/extensions/sandbox/sandboxes/e2b.py index 6e222bf070..cef5b0aebd 100644 --- a/src/agents/extensions/sandbox/sandboxes/e2b.py +++ b/src/agents/extensions/sandbox/sandboxes/e2b.py @@ -645,7 +645,9 @@ async def mkdir(self, path: Path | str, *, parents: bool = False) -> None: def _tar_exclude_args(self) -> list[str]: excludes: list[str] = [] - for rel in sorted(self.state.manifest.ephemeral_entry_paths(), key=lambda p: p.as_posix()): + for rel in sorted( + self.state.manifest.ephemeral_persistence_paths(), key=lambda p: p.as_posix() + ): rel_posix = rel.as_posix().lstrip("/") if not rel_posix or rel_posix in {".", "/"}: continue @@ -688,50 +690,71 @@ async def _run_persist_workspace_command(self, tar_cmd: str) -> str: raise WorkspaceArchiveReadError(path=Path(self.state.manifest.root), cause=e) from e async def persist_workspace(self) -> io.IOBase: + def _error_context_summary(error: WorkspaceArchiveReadError) -> dict[str, str]: + summary = {"message": error.message} + if error.cause is not None: + summary["cause_type"] = type(error.cause).__name__ + summary["cause"] = str(error.cause) + return summary + root = Path(self.state.manifest.root) excludes = " ".join(self._tar_exclude_args()) tar_cmd = f"tar {excludes} -C {shlex.quote(str(root))} -cf - . | base64 -w0" - mounts: list[tuple[Mount, Path]] = [] - for dest, entry in self.state.manifest.entries.items(): - if isinstance(entry, Mount): - dest_path = dest if isinstance(dest, Path) else Path(dest) - mount_path = entry._resolve_mount_path(self, dest_path) - mounts.append((entry, mount_path)) unmounted_mounts: list[tuple[Mount, Path]] = [] - for mount_entry, mount_path in mounts: + unmount_error: WorkspaceArchiveReadError | None = None + for mount_entry, mount_path in self.state.manifest.ephemeral_mount_targets(): try: await mount_entry.unmount_path(self, mount_path) except Exception as e: - raise WorkspaceArchiveReadError(path=root, cause=e) from e + unmount_error = WorkspaceArchiveReadError(path=root, cause=e) + break unmounted_mounts.append((mount_entry, mount_path)) snapshot_error: WorkspaceArchiveReadError | None = None raw: bytes | None = None - try: - encoded = await self._run_persist_workspace_command(tar_cmd) + if unmount_error is None: try: - raw = base64.b64decode(encoded.encode("utf-8"), validate=True) - except (binascii.Error, ValueError) as e: - raise WorkspaceArchiveReadError( - path=root, - context={"reason": "snapshot_invalid_base64"}, - cause=e, - ) from e - except WorkspaceArchiveReadError as e: - snapshot_error = e + encoded = await self._run_persist_workspace_command(tar_cmd) + try: + raw = base64.b64decode(encoded.encode("utf-8"), validate=True) + except (binascii.Error, ValueError) as e: + raise WorkspaceArchiveReadError( + path=root, + context={"reason": "snapshot_invalid_base64"}, + cause=e, + ) from e + except WorkspaceArchiveReadError as e: + snapshot_error = e remount_error: WorkspaceArchiveReadError | None = None for mount_entry, mount_path in reversed(unmounted_mounts): try: await mount_entry.mount(self, mount_path) except Exception as e: - remount_error = WorkspaceArchiveReadError(path=root, cause=e) - break + current_error = WorkspaceArchiveReadError(path=root, cause=e) + if remount_error is None: + remount_error = current_error + if unmount_error is not None: + remount_error.context["earlier_unmount_error"] = _error_context_summary( + unmount_error + ) + else: + additional_remount_errors = remount_error.context.setdefault( + "additional_remount_errors", [] + ) + assert isinstance(additional_remount_errors, list) + additional_remount_errors.append(_error_context_summary(current_error)) - if snapshot_error is not None: - raise snapshot_error if remount_error is not None: + if snapshot_error is not None: + remount_error.context["snapshot_error_before_remount_corruption"] = ( + _error_context_summary(snapshot_error) + ) raise remount_error + if unmount_error is not None: + raise unmount_error + if snapshot_error is not None: + raise snapshot_error assert raw is not None return io.BytesIO(raw) diff --git a/src/agents/extensions/sandbox/sandboxes/modal.py b/src/agents/extensions/sandbox/sandboxes/modal.py index 7f01187760..ab52b92472 100644 --- a/src/agents/extensions/sandbox/sandboxes/modal.py +++ b/src/agents/extensions/sandbox/sandboxes/modal.py @@ -493,7 +493,7 @@ async def _persist_workspace_via_snapshot_filesystem(self) -> io.IOBase: # Feature not present in this Modal SDK version; fall back to tar implementation. return await self._persist_workspace_via_tar() - skip = self.state.manifest.ephemeral_entry_paths() + skip = self.state.manifest.ephemeral_persistence_paths() # Modal's snapshot_filesystem does not support excluding paths. To # preserve the semantics of "ephemeral manifest entries are not @@ -621,7 +621,7 @@ def _restore_ephemeral() -> None: async def _persist_workspace_via_tar(self) -> io.IOBase: # Existing tar implementation extracted so snapshot_filesystem mode can fall back cleanly. root = Path(self.state.manifest.root) - skip = self.state.manifest.ephemeral_entry_paths() + skip = self.state.manifest.ephemeral_persistence_paths() excludes: list[str] = [] for rel in sorted(skip, key=lambda p: p.as_posix()): @@ -762,7 +762,7 @@ async def _hydrate_workspace_via_tar(self, data: io.IOBase) -> None: continue if should_skip_tar_member( name, - skip_rel_paths=self.state.manifest.ephemeral_entry_paths(), + skip_rel_paths=self.state.manifest.ephemeral_persistence_paths(), root_name=None, ): continue diff --git a/src/agents/sandbox/entries/mounts/base.py b/src/agents/sandbox/entries/mounts/base.py index a18b461f52..ab93c2bc94 100644 --- a/src/agents/sandbox/entries/mounts/base.py +++ b/src/agents/sandbox/entries/mounts/base.py @@ -88,6 +88,13 @@ def _resolve_mount_path( dest: Path, ) -> Path: manifest_root = Path(getattr(session.state.manifest, "root", "/")) + return self._resolve_mount_path_for_root(manifest_root, dest) + + def _resolve_mount_path_for_root( + self, + manifest_root: Path, + dest: Path, + ) -> Path: if self.mount_path is not None: mount_path = Path(self.mount_path) if mount_path.is_absolute(): diff --git a/src/agents/sandbox/manifest.py b/src/agents/sandbox/manifest.py index 09b735771a..398e2b47a3 100644 --- a/src/agents/sandbox/manifest.py +++ b/src/agents/sandbox/manifest.py @@ -7,7 +7,7 @@ from pydantic import BaseModel, Field, field_serializer, field_validator from typing_extensions import assert_never -from .entries import BaseEntry, Dir +from .entries import BaseEntry, Dir, Mount, resolve_workspace_path from .errors import InvalidManifestPathError from .manifest_render import render_manifest_description from .types import Group, User @@ -92,6 +92,34 @@ def ephemeral_entry_paths(self, depth: int | None = 1) -> set[Path]: _ = depth return {path for path, artifact in self.iter_entries() if artifact.ephemeral} + def ephemeral_mount_targets(self) -> list[tuple[Mount, Path]]: + root = Path(self.root) + mounts: list[tuple[Mount, Path]] = [] + for rel_path, artifact in self.iter_entries(): + if not isinstance(artifact, Mount) or not artifact.ephemeral: + continue + dest = resolve_workspace_path(root, rel_path) + mount_path = artifact._resolve_mount_path_for_root(root, dest) + normalized_mount_path = self._normalize_in_workspace_path(root, mount_path) + if normalized_mount_path is not None: + mount_path = normalized_mount_path + mounts.append((artifact, mount_path)) + mounts.sort(key=lambda item: len(item[1].parts), reverse=True) + return mounts + + def ephemeral_persistence_paths(self, depth: int | None = 1) -> set[Path]: + _ = depth + root = Path(self.root) + skip = self.ephemeral_entry_paths(depth=depth) + for _mount, mount_path in self.ephemeral_mount_targets(): + try: + rel_mount_path = mount_path.relative_to(root) + except ValueError: + continue + if rel_mount_path.parts: + skip.add(rel_mount_path) + return skip + @staticmethod def _coerce_rel_path(path: str | Path) -> Path: return path if isinstance(path, Path) else Path(path) @@ -103,6 +131,38 @@ def _validate_rel_path(rel: Path) -> None: if ".." in rel.parts: raise InvalidManifestPathError(rel=rel, reason="escape_root") + @staticmethod + def _normalize_rel_path_within_root(rel: Path, *, original: Path) -> Path: + if rel.is_absolute(): + raise InvalidManifestPathError(rel=original, reason="absolute") + + normalized_parts: list[str] = [] + for part in rel.parts: + if part in ("", "."): + continue + if part == "..": + if not normalized_parts: + raise InvalidManifestPathError(rel=original, reason="escape_root") + normalized_parts.pop() + continue + normalized_parts.append(part) + + return Path(*normalized_parts) + + @classmethod + def _normalize_in_workspace_path(cls, root: Path, path: Path) -> Path | None: + if not path.is_absolute(): + normalized_rel = cls._normalize_rel_path_within_root(path, original=path) + return root / normalized_rel if normalized_rel.parts else root + + try: + rel_path = path.relative_to(root) + except ValueError: + return None + + normalized_rel = cls._normalize_rel_path_within_root(rel_path, original=path) + return root / normalized_rel if normalized_rel.parts else root + def iter_entries(self) -> Iterator[tuple[Path, BaseEntry]]: stack = [ (self._coerce_rel_path(path), artifact) diff --git a/src/agents/sandbox/sandboxes/docker.py b/src/agents/sandbox/sandboxes/docker.py index f292b1f00b..6e9128c6bf 100644 --- a/src/agents/sandbox/sandboxes/docker.py +++ b/src/agents/sandbox/sandboxes/docker.py @@ -405,47 +405,106 @@ async def exists(self) -> bool: retry_if=lambda exc, self: exception_chain_has_status_code(exc, TRANSIENT_HTTP_STATUS_CODES) ) async def persist_workspace(self) -> io.IOBase: - skip = self.state.manifest.ephemeral_entry_paths() + def _error_context_summary(error: WorkspaceArchiveReadError) -> dict[str, str]: + summary = {"message": error.message} + if error.cause is not None: + summary["cause_type"] = type(error.cause).__name__ + summary["cause"] = str(error.cause) + return summary + + skip = self.state.manifest.ephemeral_persistence_paths() root = Path(self.state.manifest.root) - staging_parent, staging_workspace = await self._stage_workspace_copy() - - try: - for rel_path in skip: - await self._rm_best_effort(staging_workspace / rel_path) - - bits, _ = self._container.get_archive(str(staging_workspace)) - root_name = root.name or "workspace" - if not skip: - return IteratorIO(it=bits) - - in_stream = IteratorIO(it=bits) - out_stream = tempfile.SpooledTemporaryFile(max_size=16 * 1024 * 1024, mode="w+b") + unmounted_mounts: list[tuple[Mount, Path]] = [] + unmount_error: WorkspaceArchiveReadError | None = None + for mount_entry, mount_path in self.state.manifest.ephemeral_mount_targets(): try: - with ( - tarfile.open(fileobj=in_stream, mode="r|*") as in_tar, - tarfile.open(fileobj=out_stream, mode="w") as out_tar, - ): - for member in in_tar: - if should_skip_tar_member( - member.name, skip_rel_paths=skip, root_name=root_name - ): - continue - fileobj = in_tar.extractfile(member) if member.isreg() else None - out_tar.addfile(member, fileobj) - if fileobj is not None: - fileobj.close() - except (tarfile.TarError, OSError) as e: - out_stream.close() - raise WorkspaceArchiveReadError(path=root, cause=e) from e - - out_stream.seek(0) - return cast(io.IOBase, out_stream) - except docker.errors.NotFound as e: - raise WorkspaceArchiveReadError(path=root, cause=e) from e - except docker.errors.APIError as e: - raise WorkspaceArchiveReadError(path=root, cause=e) from e - finally: - await self._rm_best_effort(staging_parent) + await mount_entry.unmount_path(self, mount_path) + except Exception as e: + unmount_error = WorkspaceArchiveReadError(path=root, cause=e) + break + unmounted_mounts.append((mount_entry, mount_path)) + + snapshot_error: WorkspaceArchiveReadError | None = None + archive: io.IOBase | None = None + staging_parent: Path | None = None + if unmount_error is None: + try: + try: + staging_parent, staging_workspace = await self._stage_workspace_copy() + for rel_path in skip: + await self._rm_best_effort(staging_workspace / rel_path) + + bits, _ = self._container.get_archive(str(staging_workspace)) + root_name = root.name or "workspace" + if not skip: + archive = IteratorIO(it=bits) + else: + in_stream = IteratorIO(it=bits) + out_stream = tempfile.SpooledTemporaryFile( + max_size=16 * 1024 * 1024, mode="w+b" + ) + try: + with ( + tarfile.open(fileobj=in_stream, mode="r|*") as in_tar, + tarfile.open(fileobj=out_stream, mode="w") as out_tar, + ): + for member in in_tar: + if should_skip_tar_member( + member.name, skip_rel_paths=skip, root_name=root_name + ): + continue + fileobj = in_tar.extractfile(member) if member.isreg() else None + out_tar.addfile(member, fileobj) + if fileobj is not None: + fileobj.close() + except (tarfile.TarError, OSError) as e: + out_stream.close() + raise WorkspaceArchiveReadError(path=root, cause=e) from e + + out_stream.seek(0) + archive = cast(io.IOBase, out_stream) + except docker.errors.NotFound as e: + snapshot_error = WorkspaceArchiveReadError(path=root, cause=e) + except docker.errors.APIError as e: + snapshot_error = WorkspaceArchiveReadError(path=root, cause=e) + except WorkspaceArchiveReadError as e: + snapshot_error = e + finally: + if staging_parent is not None: + await self._rm_best_effort(staging_parent) + + remount_error: WorkspaceArchiveReadError | None = None + for mount_entry, mount_path in reversed(unmounted_mounts): + try: + await mount_entry.mount(self, mount_path) + except Exception as e: + current_error = WorkspaceArchiveReadError(path=root, cause=e) + if remount_error is None: + remount_error = current_error + if unmount_error is not None: + remount_error.context["earlier_unmount_error"] = _error_context_summary( + unmount_error + ) + else: + additional_remount_errors = remount_error.context.setdefault( + "additional_remount_errors", [] + ) + assert isinstance(additional_remount_errors, list) + additional_remount_errors.append(_error_context_summary(current_error)) + + if remount_error is not None: + if snapshot_error is not None: + remount_error.context["snapshot_error_before_remount_corruption"] = ( + _error_context_summary(snapshot_error) + ) + raise remount_error + if unmount_error is not None: + raise unmount_error + if snapshot_error is not None: + raise snapshot_error + + assert archive is not None + return archive async def hydrate_workspace(self, data: io.IOBase) -> None: root = self.state.manifest.root diff --git a/src/agents/sandbox/sandboxes/unix_local.py b/src/agents/sandbox/sandboxes/unix_local.py index ce6ca8dcd0..04f9e7e55a 100644 --- a/src/agents/sandbox/sandboxes/unix_local.py +++ b/src/agents/sandbox/sandboxes/unix_local.py @@ -460,7 +460,7 @@ async def persist_workspace(self) -> io.IOBase: path=root, context={"reason": "workspace_root_not_found"} ) - skip = self.state.manifest.ephemeral_entry_paths() + skip = self.state.manifest.ephemeral_persistence_paths() buf = io.BytesIO() try: with tarfile.open(fileobj=buf, mode="w") as tar: diff --git a/tests/extensions/test_sandbox_e2b.py b/tests/extensions/test_sandbox_e2b.py index ca6dad1858..83ba1be614 100644 --- a/tests/extensions/test_sandbox_e2b.py +++ b/tests/extensions/test_sandbox_e2b.py @@ -5,14 +5,13 @@ import tarfile import uuid from pathlib import Path -from typing import Literal import pytest from pydantic import PrivateAttr from agents.extensions.sandbox.sandboxes.e2b import E2BSandboxSession, E2BSandboxSessionState from agents.sandbox import Manifest -from agents.sandbox.entries import Mount +from agents.sandbox.entries import Dir, Mount from agents.sandbox.errors import ( WorkspaceArchiveReadError, WorkspaceArchiveWriteError, @@ -100,19 +99,44 @@ def is_running(self, request_timeout: float | None = None) -> bool: class _RecordingMount(Mount): - type: Literal["recording_mount"] = "recording_mount" + type: str = "recording_mount" _mounted_paths: list[Path] = PrivateAttr(default_factory=list) _unmounted_paths: list[Path] = PrivateAttr(default_factory=list) + _events: list[tuple[str, str]] = PrivateAttr(default_factory=list) + + def bind_events(self, events: list[tuple[str, str]]) -> _RecordingMount: + self._events = events + return self async def _mount(self, session: object, path: Path) -> None: _ = session + self._events.append(("mount", str(path))) self._mounted_paths.append(path) async def _unmount(self, session: object, path: Path) -> None: _ = session + self._events.append(("unmount", str(path))) self._unmounted_paths.append(path) +class _FailingUnmountMount(_RecordingMount): + type: str = "failing_unmount_mount" + + async def _unmount(self, session: object, path: Path) -> None: + _ = session + self._events.append(("unmount_fail", str(path))) + raise RuntimeError("boom while unmounting second mount") + + +class _FailingRemountMount(_RecordingMount): + type: str = "failing_remount_mount" + + async def _mount(self, session: object, path: Path) -> None: + _ = session + self._events.append(("mount_fail", str(path))) + raise RuntimeError("boom while remounting second mount") + + def _session(*, workspace_root_ready: bool = False) -> tuple[E2BSandboxSession, _FakeE2BSandbox]: sandbox = _FakeE2BSandbox() state = E2BSandboxSessionState( @@ -307,3 +331,122 @@ async def test_e2b_persist_workspace_remounts_mounts_after_snapshot() -> None: assert archive.read() == b"fake-tar-bytes" assert mount._unmounted_paths == [Path("/workspace/mount")] assert mount._mounted_paths == [Path("/workspace/mount")] + + +@pytest.mark.asyncio +async def test_e2b_persist_workspace_uses_nested_mount_targets_and_resolved_excludes() -> None: + parent_mount = _RecordingMount(mount_path=Path("repo")) + child_mount = _RecordingMount(mount_path=Path("repo/sub")) + events: list[tuple[str, str]] = [] + sandbox = _FakeE2BSandbox() + sandbox.commands.exec_root_ready = True + sandbox.commands.next_result = _FakeE2BResult( + stdout=base64.b64encode(b"fake-tar-bytes").decode("ascii") + ) + state = E2BSandboxSessionState( + session_id=uuid.uuid4(), + manifest=Manifest( + root="/workspace", + entries={ + "parent": parent_mount.bind_events(events), + "nested": Dir(children={"child": child_mount.bind_events(events)}), + }, + ), + snapshot=NoopSnapshot(id="snapshot"), + sandbox_id=sandbox.sandbox_id, + workspace_root_ready=True, + ) + session = E2BSandboxSession.from_state(state, sandbox=sandbox) + + archive = await session.persist_workspace() + + assert archive.read() == b"fake-tar-bytes" + assert [path for kind, path in events if kind == "unmount"] == [ + "/workspace/repo/sub", + "/workspace/repo", + ] + assert [path for kind, path in events if kind == "mount"] == [ + "/workspace/repo", + "/workspace/repo/sub", + ] + tar_command = str(sandbox.commands.calls[-1]["command"]) + assert "--exclude=repo" in tar_command + assert "--exclude=./repo" in tar_command + assert "--exclude=repo/sub" in tar_command + assert "--exclude=./repo/sub" in tar_command + + +@pytest.mark.asyncio +async def test_e2b_persist_workspace_remounts_prior_mounts_after_unmount_failure() -> None: + events: list[tuple[str, str]] = [] + sandbox = _FakeE2BSandbox() + sandbox.commands.exec_root_ready = True + state = E2BSandboxSessionState( + session_id=uuid.uuid4(), + manifest=Manifest( + root="/workspace", + entries={ + "repo": Dir( + children={ + "mount1": _RecordingMount().bind_events(events), + "mount2": _FailingUnmountMount().bind_events(events), + } + ) + }, + ), + snapshot=NoopSnapshot(id="snapshot"), + sandbox_id=sandbox.sandbox_id, + workspace_root_ready=True, + ) + session = E2BSandboxSession.from_state(state, sandbox=sandbox) + + with pytest.raises(WorkspaceArchiveReadError): + await session.persist_workspace() + + assert [kind for kind, _path in events] == [ + "unmount", + "unmount_fail", + "mount", + ] + assert sandbox.commands.calls == [] + + +@pytest.mark.asyncio +async def test_e2b_persist_workspace_keeps_remounting_and_raises_remount_error_first() -> None: + events: list[tuple[str, str]] = [] + sandbox = _FakeE2BSandbox() + sandbox.commands.exec_root_ready = True + sandbox.commands.next_result = _FakeE2BResult(stderr="tar failed", exit_code=2) + state = E2BSandboxSessionState( + session_id=uuid.uuid4(), + manifest=Manifest( + root="/workspace", + entries={ + "repo": Dir( + children={ + "a": _RecordingMount().bind_events(events), + "b": _FailingRemountMount().bind_events(events), + } + ) + }, + ), + snapshot=NoopSnapshot(id="snapshot"), + sandbox_id=sandbox.sandbox_id, + workspace_root_ready=True, + ) + session = E2BSandboxSession.from_state(state, sandbox=sandbox) + + with pytest.raises(WorkspaceArchiveReadError) as exc_info: + await session.persist_workspace() + + assert isinstance(exc_info.value.cause, RuntimeError) + assert str(exc_info.value.cause) == "boom while remounting second mount" + assert exc_info.value.context["snapshot_error_before_remount_corruption"] == { + "message": "failed to read archive for path: /workspace", + } + assert [kind for kind, _path in events] == [ + "unmount", + "unmount", + "mount_fail", + "mount", + ] diff --git a/tests/extensions/test_sandbox_modal.py b/tests/extensions/test_sandbox_modal.py index d68edf64c8..71c3fa259d 100644 --- a/tests/extensions/test_sandbox_modal.py +++ b/tests/extensions/test_sandbox_modal.py @@ -5,13 +5,14 @@ import sys import types from collections.abc import Callable +from pathlib import Path from typing import Any import pytest from agents.sandbox import Manifest -from agents.sandbox.entries import File -from agents.sandbox.errors import WorkspaceArchiveReadError +from agents.sandbox.entries import File, GCSMount +from agents.sandbox.errors import InvalidManifestPathError, WorkspaceArchiveReadError from agents.sandbox.manifest import Environment from agents.sandbox.types import ExecResult @@ -213,3 +214,196 @@ async def _fake_call_modal( assert exc_info.value.context["reason"] == "snapshot_filesystem_failed" assert sandbox.restore_payloads == [b"ephemeral-backup"] + + +@pytest.mark.asyncio +async def test_modal_snapshot_filesystem_uses_resolved_mount_paths_for_backup_and_removal( + monkeypatch: pytest.MonkeyPatch, +) -> None: + modal_module, _create_calls, _registry_tags = _load_modal_module(monkeypatch) + + class _FakeRestoreProcess: + def __init__(self) -> None: + self.stderr = io.BytesIO(b"") + self.stdin = self._FakeStdin() + + class _FakeStdin: + def write(self, data: bytes) -> None: + _ = data + + def write_eof(self) -> None: + return + + def drain(self) -> None: + return + + def wait(self) -> int: + return 0 + + class _FakeSnapshotSandbox: + object_id = "sb-123" + + def snapshot_filesystem(self) -> str: + return "snap-123" + + def exec(self, *command: object, **kwargs: object) -> _FakeRestoreProcess: + _ = kwargs + assert command[:3] == ("tar", "xf", "-") + return _FakeRestoreProcess() + + sandbox = _FakeSnapshotSandbox() + state = modal_module.ModalSandboxSessionState( + manifest=Manifest( + root="/workspace", + entries={"logical": GCSMount(bucket="bucket", mount_path=Path("actual"))}, + ), + snapshot=modal_module.resolve_snapshot(None, "snapshot"), + app_name="sandbox-tests", + sandbox_id=sandbox.object_id, + workspace_persistence="snapshot_filesystem", + ) + session = modal_module.ModalSandboxSession.from_state(state, sandbox=sandbox) + commands: list[list[str]] = [] + + async def _fake_exec( + *command: object, + timeout: float | None = None, + shell: bool | list[str] = True, + user: object | None = None, + ) -> ExecResult: + _ = (timeout, shell, user) + rendered = [str(part) for part in command] + commands.append(rendered) + if rendered[:2] == ["sh", "-lc"]: + return ExecResult(stdout=b"ephemeral-backup", stderr=b"", exit_code=0) + if rendered[:3] == ["rm", "-rf", "--"]: + return ExecResult(stdout=b"", stderr=b"", exit_code=0) + raise AssertionError(f"unexpected command: {rendered!r}") + + async def _fake_call_modal( + fn: Callable[..., object], + *args: object, + call_timeout: float | None = None, + **kwargs: object, + ) -> object: + _ = call_timeout + return fn(*args, **kwargs) + + monkeypatch.setattr(session, "exec", _fake_exec) + monkeypatch.setattr(session, "_call_modal", _fake_call_modal) + + archive = await session.persist_workspace() + + assert archive.read() == modal_module._encode_snapshot_filesystem_ref(snapshot_id="snap-123") + assert commands[0][0:2] == ["sh", "-lc"] + assert "actual" in commands[0][2] + assert "logical" in commands[0][2] + assert commands[1] == ["rm", "-rf", "--", "/workspace/actual", "/workspace/logical"] + + +@pytest.mark.asyncio +async def test_modal_tar_persist_uses_resolved_mount_paths_for_excludes( + monkeypatch: pytest.MonkeyPatch, +) -> None: + modal_module, _create_calls, _registry_tags = _load_modal_module(monkeypatch) + + state = modal_module.ModalSandboxSessionState( + manifest=Manifest( + root="/workspace", + entries={"logical": GCSMount(bucket="bucket", mount_path=Path("actual"))}, + ), + snapshot=modal_module.resolve_snapshot(None, "snapshot"), + app_name="sandbox-tests", + ) + session = modal_module.ModalSandboxSession.from_state(state, sandbox=None) + commands: list[list[str]] = [] + + async def _fake_exec( + *command: object, + timeout: float | None = None, + shell: bool | list[str] = True, + user: object | None = None, + ) -> ExecResult: + _ = (timeout, shell, user) + rendered = [str(part) for part in command] + commands.append(rendered) + return ExecResult(stdout=b"tar-bytes", stderr=b"", exit_code=0) + + monkeypatch.setattr(session, "exec", _fake_exec) + + archive = await session.persist_workspace() + + assert archive.read() == b"tar-bytes" + assert commands == [ + [ + "tar", + "cf", + "-", + "--exclude", + "./actual", + "--exclude", + "./logical", + "-C", + "/workspace", + ".", + ] + ] + + +@pytest.mark.asyncio +async def test_modal_snapshot_filesystem_rejects_escaping_mount_paths_before_exec( + monkeypatch: pytest.MonkeyPatch, +) -> None: + modal_module, _create_calls, _registry_tags = _load_modal_module(monkeypatch) + + class _FakeSnapshotSandbox: + object_id = "sb-123" + + def __init__(self) -> None: + self.snapshot_calls = 0 + + def snapshot_filesystem(self) -> str: + self.snapshot_calls += 1 + return "snap-123" + + sandbox = _FakeSnapshotSandbox() + state = modal_module.ModalSandboxSessionState( + manifest=Manifest( + root="/workspace", + entries={"logical": GCSMount(bucket="bucket", mount_path=Path("/workspace/../../tmp"))}, + ), + snapshot=modal_module.resolve_snapshot(None, "snapshot"), + app_name="sandbox-tests", + sandbox_id=sandbox.object_id, + workspace_persistence="snapshot_filesystem", + ) + session = modal_module.ModalSandboxSession.from_state(state, sandbox=sandbox) + commands: list[list[str]] = [] + + async def _fake_exec( + *command: object, + timeout: float | None = None, + shell: bool | list[str] = True, + user: object | None = None, + ) -> ExecResult: + _ = (timeout, shell, user) + commands.append([str(part) for part in command]) + raise AssertionError("exec() should not run for escaping mount paths") + + async def _fake_call_modal( + fn: Callable[..., object], + *args: object, + call_timeout: float | None = None, + **kwargs: object, + ) -> object: + _ = (fn, args, call_timeout, kwargs) + raise AssertionError("snapshot_filesystem() should not run for escaping mount paths") + + monkeypatch.setattr(session, "exec", _fake_exec) + monkeypatch.setattr(session, "_call_modal", _fake_call_modal) + + with pytest.raises(InvalidManifestPathError, match="must not escape root"): + await session.persist_workspace() + + assert commands == [] + assert sandbox.snapshot_calls == 0 diff --git a/tests/test_sandbox_docker.py b/tests/test_sandbox_docker.py index 790b604605..92fc64643c 100644 --- a/tests/test_sandbox_docker.py +++ b/tests/test_sandbox_docker.py @@ -4,12 +4,14 @@ import io import shutil import tarfile +import uuid from collections.abc import Callable from pathlib import Path from typing import cast import docker.errors # type: ignore[import-untyped] import pytest +from pydantic import PrivateAttr import agents.sandbox.sandboxes.docker as docker_sandbox from agents.sandbox.entries import ( @@ -17,9 +19,14 @@ Dir, File, FuseMountPattern, + Mount, RcloneMountPattern, ) -from agents.sandbox.errors import ExecTimeoutError, InvalidManifestPathError +from agents.sandbox.errors import ( + ExecTimeoutError, + InvalidManifestPathError, + WorkspaceArchiveReadError, +) from agents.sandbox.manifest import Manifest from agents.sandbox.sandboxes.docker import ( DockerSandboxClient, @@ -33,16 +40,19 @@ class _FakeDockerContainer: - def __init__(self, host_root: Path) -> None: + def __init__(self, host_root: Path, *, archive_error: Exception | None = None) -> None: self._host_root = host_root self.status = "running" self.archive_calls: list[str] = [] + self.archive_error = archive_error def reload(self) -> None: return def get_archive(self, path: str) -> tuple[object, dict[str, object]]: self.archive_calls.append(path) + if self.archive_error is not None: + raise self.archive_error if path == "/workspace": raise docker.errors.APIError("root archive unsupported") @@ -72,8 +82,15 @@ def __init__(self) -> None: class _HostBackedDockerSession(DockerSandboxSession): - def __init__(self, *, host_root: Path, manifest: Manifest) -> None: - container = _FakeDockerContainer(host_root) + def __init__( + self, + *, + host_root: Path, + manifest: Manifest, + event_log: list[tuple[str, str]] | None = None, + archive_error: Exception | None = None, + ) -> None: + container = _FakeDockerContainer(host_root, archive_error=archive_error) state = DockerSandboxSessionState( manifest=manifest, snapshot=NoopSnapshot(id="snapshot"), @@ -87,6 +104,7 @@ def __init__(self, *, host_root: Path, manifest: Manifest) -> None: ) self._host_root = host_root self._fake_container = container + self._event_log = event_log if event_log is not None else [] async def _exec_internal( self, @@ -99,6 +117,7 @@ async def _exec_internal( self._host_path(cmd[2]).mkdir(parents=True, exist_ok=True) return ExecResult(stdout=b"", stderr=b"", exit_code=0) if cmd[:3] == ["cp", "-R", "--"]: + self._event_log.append(("cp", cmd[3])) src = self._host_path(cmd[3]) dst = self._host_path(cmd[4]) shutil.copytree(src, dst) @@ -120,6 +139,79 @@ def _host_path(self, path: str | Path) -> Path: return self._host_root / container_path.relative_to("/") +class _RecordingMount(Mount): + type: str = f"recording_mount_{uuid.uuid4().hex}" + remove_on_unmount: bool = True + remount_marker: str | None = None + _events: list[tuple[str, str]] = PrivateAttr(default_factory=list) + + def bind_events(self, events: list[tuple[str, str]]) -> _RecordingMount: + self._events = events + return self + + async def _mount(self, session: object, path: Path) -> None: + host_path = cast(_HostBackedDockerSession, session)._host_path(path) + host_path.mkdir(parents=True, exist_ok=True) + self._events.append(("mount", str(path))) + if self.remount_marker is not None: + (host_path / self.remount_marker).write_text("remounted", encoding="utf-8") + + async def _unmount(self, session: object, path: Path) -> None: + host_path = cast(_HostBackedDockerSession, session)._host_path(path) + self._events.append(("unmount", str(path))) + if not self.remove_on_unmount: + return + shutil.rmtree(host_path, ignore_errors=True) + + +class _FailingUnmountMount(_RecordingMount): + type: str = f"failing_unmount_mount_{uuid.uuid4().hex}" + + async def _unmount(self, session: object, path: Path) -> None: + self._events.append(("unmount_fail", str(path))) + raise RuntimeError("boom while unmounting second mount") + + +class _FailingRemountMount(_RecordingMount): + type: str = f"failing_remount_mount_{uuid.uuid4().hex}" + + async def _mount(self, session: object, path: Path) -> None: + self._events.append(("mount_fail", str(path))) + raise RuntimeError("boom while remounting second mount") + + +class _OrderSensitiveMount(_RecordingMount): + type: str = f"order_sensitive_mount_{uuid.uuid4().hex}" + require_unmounted_before: str | None = None + require_mounted_before: str | None = None + + async def _mount(self, session: object, path: Path) -> None: + if ( + self.require_mounted_before is not None + and ( + "mount", + self.require_mounted_before, + ) + not in self._events + ): + self._events.append(("mount_fail", str(path))) + raise RuntimeError("parent mount missing") + await super()._mount(session, path) + + async def _unmount(self, session: object, path: Path) -> None: + if ( + self.require_unmounted_before is not None + and ( + "unmount", + self.require_unmounted_before, + ) + not in self._events + ): + self._events.append(("unmount_fail", str(path))) + raise RuntimeError("target is busy") + await super()._unmount(session, path) + + def _archive_member_names(archive: io.IOBase) -> list[str]: payload = archive.read() if not isinstance(payload, bytes): @@ -179,6 +271,241 @@ async def test_docker_persist_workspace_prunes_ephemeral_entries_from_staged_cop assert not any(name.endswith("workspace/skip.txt") for name in names) +@pytest.mark.asyncio +async def test_docker_persist_workspace_unmounts_nested_ephemeral_mounts_before_copy( + tmp_path: Path, +) -> None: + host_root = tmp_path / "container" + workspace = host_root / "workspace" + mount_dir = workspace / "repo" / "mount" + mount_dir.mkdir(parents=True) + (mount_dir / "remote.txt").write_text("remote", encoding="utf-8") + + events: list[tuple[str, str]] = [] + mount = _RecordingMount(remount_marker="remounted.txt").bind_events(events) + session = _HostBackedDockerSession( + host_root=host_root, + manifest=Manifest( + root="/workspace", + entries={ + "repo": Dir( + children={ + "mount": mount, + } + ) + }, + ), + event_log=events, + ) + + archive = await session.persist_workspace() + + names = _archive_member_names(archive) + + assert [kind for kind, _path in events if kind in {"unmount", "cp", "mount"}] == [ + "unmount", + "cp", + "mount", + ] + assert not any(name.endswith("workspace/repo/mount/remote.txt") for name in names) + assert (mount_dir / "remounted.txt").read_text(encoding="utf-8") == "remounted" + + +@pytest.mark.asyncio +async def test_docker_persist_workspace_prunes_explicit_mount_path_from_staged_copy( + tmp_path: Path, +) -> None: + host_root = tmp_path / "container" + workspace = host_root / "workspace" + actual_mount_path = workspace / "actual" + actual_mount_path.mkdir(parents=True) + (actual_mount_path / "remote.txt").write_text("remote", encoding="utf-8") + + mount = _RecordingMount(mount_path=Path("actual"), remove_on_unmount=False) + session = _HostBackedDockerSession( + host_root=host_root, + manifest=Manifest( + root="/workspace", + entries={ + "logical": mount, + }, + ), + ) + + archive = await session.persist_workspace() + + names = _archive_member_names(archive) + + assert not any(name.endswith("workspace/actual/remote.txt") for name in names) + assert (actual_mount_path / "remote.txt").read_text(encoding="utf-8") == "remote" + + +@pytest.mark.asyncio +async def test_docker_persist_workspace_remounts_prior_mounts_after_unmount_failure( + tmp_path: Path, +) -> None: + host_root = tmp_path / "container" + workspace = host_root / "workspace" + first_mount_dir = workspace / "repo" / "mount1" + second_mount_dir = workspace / "repo" / "mount2" + first_mount_dir.mkdir(parents=True) + second_mount_dir.mkdir(parents=True) + (first_mount_dir / "remote1.txt").write_text("remote-1", encoding="utf-8") + (second_mount_dir / "remote2.txt").write_text("remote-2", encoding="utf-8") + + events: list[tuple[str, str]] = [] + session = _HostBackedDockerSession( + host_root=host_root, + manifest=Manifest( + root="/workspace", + entries={ + "repo": Dir( + children={ + "mount1": _RecordingMount(remount_marker="remounted.txt").bind_events( + events + ), + "mount2": _FailingUnmountMount().bind_events(events), + } + ) + }, + ), + event_log=events, + ) + + with pytest.raises(WorkspaceArchiveReadError): + await session.persist_workspace() + + assert [kind for kind, _path in events] == [ + "unmount", + "unmount_fail", + "mount", + ] + assert "cp" not in [kind for kind, _path in events] + assert (first_mount_dir / "remounted.txt").read_text(encoding="utf-8") == "remounted" + assert (second_mount_dir / "remote2.txt").read_text(encoding="utf-8") == "remote-2" + + +@pytest.mark.asyncio +async def test_docker_persist_workspace_unmounts_nested_mount_paths_deepest_first( + tmp_path: Path, +) -> None: + host_root = tmp_path / "container" + workspace = host_root / "workspace" + parent_mount_dir = workspace / "repo" + child_mount_dir = parent_mount_dir / "sub" + child_mount_dir.mkdir(parents=True) + (child_mount_dir / "remote.txt").write_text("remote", encoding="utf-8") + + events: list[tuple[str, str]] = [] + child_path = "/workspace/repo/sub" + parent_path = "/workspace/repo" + parent_mount = _OrderSensitiveMount( + remount_marker="parent-remounted.txt", + require_unmounted_before=child_path, + ).bind_events(events) + child_mount = _OrderSensitiveMount( + mount_path=Path("repo/sub"), + remount_marker="child-remounted.txt", + require_mounted_before=parent_path, + ).bind_events(events) + session = _HostBackedDockerSession( + host_root=host_root, + manifest=Manifest( + root="/workspace", + entries={ + "repo": parent_mount, + "child": child_mount, + }, + ), + event_log=events, + ) + + archive = await session.persist_workspace() + + names = _archive_member_names(archive) + + assert [kind for kind, _path in events] == [ + "unmount", + "unmount", + "cp", + "mount", + "mount", + ] + assert [path for kind, path in events if kind == "unmount"] == [ + child_path, + parent_path, + ] + assert [path for kind, path in events if kind == "mount"] == [ + parent_path, + child_path, + ] + assert not any(name.endswith("workspace/repo/remote.txt") for name in names) + assert not any(name.endswith("workspace/repo/sub/remote.txt") for name in names) + assert (parent_mount_dir / "parent-remounted.txt").read_text(encoding="utf-8") == "remounted" + assert (child_mount_dir / "child-remounted.txt").read_text(encoding="utf-8") == "remounted" + + +@pytest.mark.asyncio +async def test_docker_persist_workspace_keeps_remounting_and_raises_remount_error_first( + tmp_path: Path, +) -> None: + host_root = tmp_path / "container" + workspace = host_root / "workspace" + first_mount_dir = workspace / "repo" / "a" + second_mount_dir = workspace / "repo" / "b" + first_mount_dir.mkdir(parents=True) + second_mount_dir.mkdir(parents=True) + (first_mount_dir / "remote1.txt").write_text("remote-1", encoding="utf-8") + (second_mount_dir / "remote2.txt").write_text("remote-2", encoding="utf-8") + + events: list[tuple[str, str]] = [] + session = _HostBackedDockerSession( + host_root=host_root, + manifest=Manifest( + root="/workspace", + entries={ + "repo": Dir( + children={ + "a": _RecordingMount(remount_marker="a-remounted.txt").bind_events(events), + "b": _FailingRemountMount().bind_events(events), + } + ) + }, + ), + event_log=events, + archive_error=docker.errors.APIError("snapshot failed"), + ) + + with pytest.raises(WorkspaceArchiveReadError) as exc_info: + await session.persist_workspace() + + assert isinstance(exc_info.value.cause, RuntimeError) + assert str(exc_info.value.cause) == "boom while remounting second mount" + snapshot_error = cast( + dict[str, str], + exc_info.value.context["snapshot_error_before_remount_corruption"], + ) + assert snapshot_error == { + "message": "failed to read archive for path: /workspace", + "cause_type": "APIError", + "cause": "snapshot failed", + } + assert exc_info.value.context["snapshot_error_before_remount_corruption"] == snapshot_error + assert "earlier_unmount_error" not in exc_info.value.context + assert "additional_remount_errors" not in exc_info.value.context + assert snapshot_error["cause"] == "snapshot failed" + assert snapshot_error["cause_type"] == "APIError" + assert exc_info.value.context["path"] == "/workspace" + assert [kind for kind, _path in events] == [ + "unmount", + "unmount", + "cp", + "mount_fail", + "mount", + ] + assert (first_mount_dir / "a-remounted.txt").read_text(encoding="utf-8") == "remounted" + + @pytest.mark.asyncio async def test_docker_read_and_write_reject_paths_outside_workspace_root(tmp_path: Path) -> None: host_root = tmp_path / "container" diff --git a/tests/test_sandbox_extract.py b/tests/test_sandbox_extract.py index 989584ccb2..244c8fc851 100644 --- a/tests/test_sandbox_extract.py +++ b/tests/test_sandbox_extract.py @@ -8,6 +8,7 @@ import pytest +from agents.sandbox.entries import GCSMount from agents.sandbox.errors import InvalidManifestPathError, WorkspaceArchiveWriteError from agents.sandbox.files import EntryKind, FileEntry from agents.sandbox.manifest import Manifest @@ -220,6 +221,33 @@ async def test_extract_zip_rejects_symlinked_parent_paths(tmp_path: Path) -> Non await session.shutdown() +@pytest.mark.asyncio +async def test_unix_local_persist_workspace_excludes_resolved_mount_path(tmp_path: Path) -> None: + workspace_root = tmp_path / "workspace" + actual_mount_path = workspace_root / "actual" + actual_mount_path.mkdir(parents=True) + (actual_mount_path / "remote.txt").write_text("remote", encoding="utf-8") + (workspace_root / "keep.txt").write_text("keep", encoding="utf-8") + + state = UnixLocalSandboxSessionState( + manifest=Manifest( + root=str(workspace_root), + entries={"logical": GCSMount(bucket="bucket", mount_path=Path("actual"))}, + ), + snapshot=NoopSnapshot(id="noop"), + ) + session = UnixLocalSandboxSession.from_state(state) + + archive = await session.persist_workspace() + + with tarfile.open(fileobj=archive, mode="r:*") as tar: + names = set(tar.getnames()) + + assert "./keep.txt" in names + assert "./actual" not in names + assert "./actual/remote.txt" not in names + + @pytest.mark.asyncio async def test_extract_tar_reuses_directory_listings_during_symlink_checks(tmp_path: Path) -> None: workspace = tmp_path / "workspace" diff --git a/tests/test_sandbox_manifest.py b/tests/test_sandbox_manifest.py index f7a583b15f..8f840c07f8 100644 --- a/tests/test_sandbox_manifest.py +++ b/tests/test_sandbox_manifest.py @@ -52,6 +52,71 @@ def test_manifest_ephemeral_entry_paths_include_nested_children() -> None: assert manifest.ephemeral_entry_paths() == {Path("dir/tmp.txt")} +def test_manifest_ephemeral_persistence_paths_include_resolved_mount_targets() -> None: + manifest = Manifest( + root="/workspace", + entries={ + "logical": GCSMount(bucket="bucket", mount_path=Path("actual")), + "dir": Dir( + children={ + "tmp.txt": File(content=b"tmp", ephemeral=True), + } + ), + }, + ) + + assert manifest.ephemeral_persistence_paths() == { + Path("logical"), + Path("actual"), + Path("dir/tmp.txt"), + } + + +def test_manifest_ephemeral_mount_targets_sort_by_resolved_depth() -> None: + parent = GCSMount(bucket="parent", mount_path=Path("repo")) + child = GCSMount(bucket="child", mount_path=Path("repo/sub")) + manifest = Manifest( + root="/workspace", + entries={ + "parent": parent, + "nested": Dir(children={"child": child}), + }, + ) + + assert manifest.ephemeral_mount_targets() == [ + (child, Path("/workspace/repo/sub")), + (parent, Path("/workspace/repo")), + ] + + +def test_manifest_ephemeral_mount_targets_normalize_non_escaping_mount_paths() -> None: + mount = GCSMount(bucket="bucket", mount_path=Path("/workspace/repo/../actual")) + manifest = Manifest(root="/workspace", entries={"logical": mount}) + + assert manifest.ephemeral_mount_targets() == [ + (mount, Path("/workspace/actual")), + ] + assert manifest.ephemeral_persistence_paths() == { + Path("logical"), + Path("actual"), + } + + +def test_manifest_ephemeral_mount_targets_reject_escaping_mount_paths() -> None: + manifest = Manifest( + root="/workspace", + entries={ + "logical": GCSMount(bucket="bucket", mount_path=Path("/workspace/../../tmp")), + }, + ) + + with pytest.raises(InvalidManifestPathError, match="must not escape root"): + manifest.ephemeral_mount_targets() + + with pytest.raises(InvalidManifestPathError, match="must not escape root"): + manifest.ephemeral_persistence_paths() + + def test_manifest_describe_preserves_tree_rendering_after_renderer_extract() -> None: manifest = Manifest( root="/workspace", From 3f119eceb70ec1b2c51033ff62da4e646dd227fa Mon Sep 17 00:00:00 2001 From: Kazuhiro Sera Date: Sun, 15 Mar 2026 17:49:51 -0700 Subject: [PATCH 03/11] fix: raise on manifest metadata application failures (#13) --- src/agents/sandbox/entries/base.py | 4 +- tests/test_sandbox_entries.py | 60 +++++++++++++++++++++++++++++- 2 files changed, 60 insertions(+), 4 deletions(-) diff --git a/src/agents/sandbox/entries/base.py b/src/agents/sandbox/entries/base.py index 0f4a9c41df..660297a7cc 100644 --- a/src/agents/sandbox/entries/base.py +++ b/src/agents/sandbox/entries/base.py @@ -132,10 +132,10 @@ async def _apply_metadata( dest: Path, ) -> None: if self.group is not None: - await session.exec("chgrp", self.group.name, str(dest), shell=False) + await session._exec_checked_nonzero("chgrp", self.group.name, str(dest)) chmod_perms = f"{stat.S_IMODE(self.permissions.to_mode()):o}".zfill(4) - await session.exec("chmod", chmod_perms, str(dest), shell=False) + await session._exec_checked_nonzero("chmod", chmod_perms, str(dest)) @abc.abstractmethod async def apply( diff --git a/tests/test_sandbox_entries.py b/tests/test_sandbox_entries.py index bf4e81cdf4..b636c0eda9 100644 --- a/tests/test_sandbox_entries.py +++ b/tests/test_sandbox_entries.py @@ -5,12 +5,13 @@ import pytest -from agents.sandbox.entries import Dir, GitRepo, LocalFile +from agents.sandbox.entries import Dir, File, GitRepo, LocalFile +from agents.sandbox.errors import ExecNonZeroError from agents.sandbox.manifest import Manifest from agents.sandbox.session.base_sandbox_session import BaseSandboxSession from agents.sandbox.session.sandbox_session_state import SandboxSessionState from agents.sandbox.snapshot import NoopSnapshot -from agents.sandbox.types import ExecResult +from agents.sandbox.types import ExecResult, User class _RecordingSession(BaseSandboxSession): @@ -67,6 +68,29 @@ async def _exec_internal( return ExecResult(stdout=b"", stderr=b"", exit_code=0) +class _MetadataFailureSession(_RecordingSession): + def __init__( + self, + manifest: Manifest | None = None, + *, + fail_commands: set[str], + ) -> None: + super().__init__(manifest) + self.fail_commands = fail_commands + + async def _exec_internal( + self, + *command: str | Path, + timeout: float | None = None, + ) -> ExecResult: + _ = timeout + cmd = tuple(str(part) for part in command) + self.exec_calls.append(cmd) + if cmd and cmd[0] in self.fail_commands: + return ExecResult(stdout=b"", stderr=b"metadata failed", exit_code=1) + return ExecResult(stdout=b"", stderr=b"", exit_code=0) + + @pytest.mark.asyncio async def test_base_sandbox_session_uses_current_working_directory_for_local_file_sources( monkeypatch: pytest.MonkeyPatch, @@ -124,3 +148,35 @@ async def test_dir_metadata_strips_file_type_bits_before_chmod() -> None: await Dir()._apply_metadata(session, Path("/workspace/dir")) assert ("chmod", "0755", "/workspace/dir") in session.exec_calls + + +@pytest.mark.asyncio +async def test_apply_manifest_raises_on_chmod_failure() -> None: + session = _MetadataFailureSession( + Manifest(entries={"copied.txt": File(content=b"hello")}), + fail_commands={"chmod"}, + ) + + with pytest.raises(ExecNonZeroError): + await session.apply_manifest() + + +@pytest.mark.asyncio +async def test_apply_manifest_raises_on_chgrp_failure() -> None: + session = _MetadataFailureSession( + Manifest( + entries={ + "copied.txt": File( + content=b"hello", + group=User(name="sandbox-user"), + ) + } + ), + fail_commands={"chgrp"}, + ) + + with pytest.raises(ExecNonZeroError): + await session.apply_manifest() + + assert ("chgrp", "sandbox-user", "/workspace/copied.txt") in session.exec_calls + assert not any(call[0] == "chmod" for call in session.exec_calls) From da17cd51961de262fb8e206e32633e48461df234 Mon Sep 17 00:00:00 2001 From: Steve Coffey Date: Sun, 15 Mar 2026 23:26:36 -0700 Subject: [PATCH 04/11] feat: add Codex sandbox artifact support (#9) This pull request adds a dedicated Codex sandbox artifact and updates sandbox write plumbing so the GitHub release archive can be streamed directly into the target box before being extracted in place. - add target-aware Codex GitHub asset resolution for Linux and macOS, with Windows explicitly unsupported for now - auto-add a dummy-version Codex entry from the sandbox manifest at `codex_relpath` unless an explicit entry already exists there - stream write payloads through unix-local and Docker sandboxes so artifact downloads no longer need SDK-side buffering - add focused sandbox manifest, session, entry, and extract coverage for the new behavior --- examples/sandbox/docker_runner.py | 42 +-- .../extensions/sandbox/sandboxes/e2b.py | 57 ++-- .../extensions/sandbox/sandboxes/modal.py | 18 +- src/agents/sandbox/__init__.py | 2 + src/agents/sandbox/codex_config.py | 96 +++++++ src/agents/sandbox/entries/__init__.py | 2 + src/agents/sandbox/entries/codex.py | 268 ++++++++++++++++++ src/agents/sandbox/errors.py | 70 +++++ src/agents/sandbox/runtime_session_manager.py | 23 +- src/agents/sandbox/sandbox_agent.py | 4 + src/agents/sandbox/sandboxes/docker.py | 134 ++++++++- src/agents/sandbox/sandboxes/unix_local.py | 45 ++- .../sandbox/session/base_sandbox_session.py | 40 +++ src/agents/sandbox/session/sandbox_client.py | 6 + .../sandbox/session/workspace_payloads.py | 78 ++++- tests/extensions/test_sandbox_e2b.py | 8 + tests/test_sandbox_entries.py | 213 +++++++++++++- tests/test_sandbox_extract.py | 43 +++ tests/test_sandbox_manifest.py | 95 ++++++- tests/test_sandbox_runtime.py | 201 ++++++++++--- tests/test_sandbox_session_utils.py | 241 ++++++++++++++++ 21 files changed, 1571 insertions(+), 115 deletions(-) create mode 100644 src/agents/sandbox/codex_config.py create mode 100644 src/agents/sandbox/entries/codex.py diff --git a/examples/sandbox/docker_runner.py b/examples/sandbox/docker_runner.py index a5f34ccda7..64778d2e10 100644 --- a/examples/sandbox/docker_runner.py +++ b/examples/sandbox/docker_runner.py @@ -18,13 +18,13 @@ from agents import ModelSettings, Runner from agents.run import RunConfig -from agents.sandbox import SandboxAgent, SandboxRunConfig +from agents.sandbox import Manifest, SandboxAgent, SandboxRunConfig +from agents.sandbox.entries import File from agents.sandbox.sandboxes.docker import DockerSandboxClient, DockerSandboxClientOptions if __package__ is None or __package__ == "": sys.path.insert(0, str(Path(__file__).resolve().parents[2])) -from examples.sandbox.misc.example_support import text_manifest from examples.sandbox.misc.workspace_shell import WorkspaceShellCapability DEFAULT_QUESTION = "Summarize this sandbox project in 2 sentences." @@ -40,20 +40,25 @@ def _stream_event_banner(event_name: str) -> str | None: async def main(model: str, question: str) -> None: # A manifest is the starting file tree for the sandbox workspace. - # Each key is a path inside the workspace and each value is the file content. - # `text_manifest()` keeps small text examples readable by hiding the bytes boilerplate. - manifest = text_manifest( - { - "README.md": ( - "# Demo Project\n\n" - "This sandbox contains a tiny demo project for the sandbox runner.\n" - "The goal is to show how Runner can prepare a Docker-backed workspace.\n" + # Each key is a path inside the workspace and each value is a concrete manifest entry. + manifest = Manifest( + entries={ + "README.md": File( + content=( + b"# Demo Project\n\n" + b"This sandbox contains a tiny demo project for the sandbox runner.\n" + b"The goal is to show how Runner can prepare a Docker-backed workspace.\n" + ) ), - "src/app.py": 'def greet(name: str) -> str:\n return f"Hello, {name}!"\n', - "docs/notes.md": ( - "# Notes\n\n" - "- The example is intentionally minimal.\n" - "- The model should inspect files through the shell tool.\n" + "src/app.py": File( + content=b'def greet(name: str) -> str:\n return f"Hello, {name}!"\n' + ), + "docs/notes.md": File( + content=( + b"# Notes\n\n" + b"- The example is intentionally minimal.\n" + b"- The model should inspect files through the shell tool.\n" + ) ), } ) @@ -88,8 +93,13 @@ async def main(model: str, question: str) -> None: # We pass the same manifest here so the container knows which files to materialize. session = await docker_client.create( manifest=manifest, - options=DockerSandboxClientOptions(image="python:3.11-slim"), + codex=agent.codex, + options=DockerSandboxClientOptions(image="python:3.14-slim"), ) + await session.start() + + print(await session.ls(".codex/codex")) + try: # `async with session` keeps the example on the public session lifecycle API. # `Runner` reuses the already-running session without starting it a second time. diff --git a/src/agents/extensions/sandbox/sandboxes/e2b.py b/src/agents/extensions/sandbox/sandboxes/e2b.py index cef5b0aebd..4282493733 100644 --- a/src/agents/extensions/sandbox/sandboxes/e2b.py +++ b/src/agents/extensions/sandbox/sandboxes/e2b.py @@ -15,6 +15,11 @@ from pydantic import BaseModel, Field +from ....sandbox.codex_config import ( + CodexConfig, + apply_codex_to_manifest, + apply_codex_to_session_state, +) from ....sandbox.entries import Mount, resolve_workspace_path from ....sandbox.errors import ( ExecNonZeroError, @@ -54,6 +59,9 @@ def write( def remove(self, path: str, request_timeout: float | None = None) -> object: raise NotImplementedError + def make_dir(self, path: str, request_timeout: float | None = None) -> object: + raise NotImplementedError + def read(self, path: str, format: str = "bytes") -> object: raise NotImplementedError @@ -206,6 +214,15 @@ def _sandbox_remove_file( return _as_sandbox_api(sandbox).files.remove(path, request_timeout=request_timeout) +def _sandbox_make_dir( + sandbox: object, + path: str, + *, + request_timeout: float | None = None, +) -> object: + return _as_sandbox_api(sandbox).files.make_dir(path, request_timeout=request_timeout) + + def _sandbox_read_file(sandbox: object, path: str, *, format: str = "bytes") -> object: return _as_sandbox_api(sandbox).files.read(path, format=format) @@ -370,7 +387,7 @@ class E2BSandboxSession(BaseSandboxSession): """E2B-backed sandbox session implementation.""" state: E2BSandboxSessionState - _sandbox: object + _sandbox: _E2BSandboxAPI _skip_start: bool _workspace_root_ready: bool _resume_preserves_system_state: bool @@ -382,7 +399,7 @@ def __init__( sandbox: object, ) -> None: self.state = state - self._sandbox = sandbox + self._sandbox = _as_sandbox_api(sandbox) self._skip_start = False self._workspace_root_ready = state.workspace_root_ready self._resume_preserves_system_state = False @@ -414,36 +431,23 @@ def _coerce_exec_timeout(self, timeout_s: float | None) -> float: return float(timeout_s) async def _ensure_dir(self, path: Path, *, reason: str) -> None: - """E2B does not support `mkdir`; write a marker file to create directories.""" + """Create a directory using the E2B Files API.""" if path == Path("/"): return - marker_path = path / ".uc_dir" try: await _maybe_await( - _sandbox_write_file( + _sandbox_make_dir( self._sandbox, - str(marker_path), - b"", - request_timeout=self.state.timeouts.file_upload_s, + str(path), + request_timeout=self.state.timeouts.fast_op_s, ) ) except Exception as e: # pragma: no cover - exercised via unit tests with fakes raise WorkspaceArchiveWriteError(path=path, context={"reason": reason}, cause=e) from e - finally: - try: - await _maybe_await( - _sandbox_remove_file( - self._sandbox, - str(marker_path), - request_timeout=self.state.timeouts.cleanup_s, - ) - ) - except Exception: - pass async def _ensure_workspace_root(self) -> None: - """Create the workspace root so file operations can materialize the manifest.""" - await self._ensure_dir(Path(self.state.manifest.root), reason="root_marker_failed") + """Ensure the workspace root exists before materialization starts.""" + await self._ensure_dir(Path(self.state.manifest.root), reason="root_make_failed") async def _prepare_workspace_root_for_exec(self) -> None: """Create the workspace root through the command API before using it as `cwd`.""" @@ -867,12 +871,13 @@ async def create( *, snapshot: SnapshotSpec | None = None, manifest: Manifest | None = None, + codex: bool | CodexConfig = False, options: E2BSandboxClientOptions, ) -> SandboxSession: if options is None: raise ValueError("E2BSandboxClient.create requires options") - manifest = manifest or Manifest() + manifest = apply_codex_to_manifest(manifest, codex) sandbox_type = _coerce_sandbox_type(options.sandbox_type) timeouts_in = options.timeouts @@ -926,9 +931,15 @@ async def delete(self, session: SandboxSession) -> SandboxSession: raise TypeError("E2BSandboxClient.delete expects an E2BSandboxSession") return session - async def resume(self, state: SandboxSessionState) -> SandboxSession: + async def resume( + self, + state: SandboxSessionState, + *, + codex: bool | CodexConfig = False, + ) -> SandboxSession: if not isinstance(state, E2BSandboxSessionState): raise TypeError("E2BSandboxClient.resume expects an E2BSandboxSessionState") + state = apply_codex_to_session_state(state, codex) sandbox_type = _coerce_sandbox_type(state.sandbox_type) SandboxClass = _import_sandbox_class(sandbox_type) diff --git a/src/agents/extensions/sandbox/sandboxes/modal.py b/src/agents/extensions/sandbox/sandboxes/modal.py index ab52b92472..006e9a0eaa 100644 --- a/src/agents/extensions/sandbox/sandboxes/modal.py +++ b/src/agents/extensions/sandbox/sandboxes/modal.py @@ -30,6 +30,11 @@ import modal from modal.container_process import ContainerProcess +from ....sandbox.codex_config import ( + CodexConfig, + apply_codex_to_manifest, + apply_codex_to_session_state, +) from ....sandbox.entries import resolve_workspace_path from ....sandbox.errors import ( ExecTimeoutError, @@ -836,6 +841,7 @@ async def create( *, snapshot: SnapshotSpec | None = None, manifest: Manifest | None = None, + codex: bool | CodexConfig = False, options: ModalSandboxClientOptions, ) -> SandboxSession: """ @@ -896,8 +902,7 @@ async def create( "snapshot_filesystem_restore_timeout_s to be a number" ) - if manifest is None: - manifest = Manifest() + manifest = apply_codex_to_manifest(manifest, codex) session_id = uuid.uuid4() state_image_id: str | None = None @@ -1001,10 +1006,15 @@ async def delete(self, session: SandboxSession) -> SandboxSession: return session - async def resume(self, state: SandboxSessionState) -> SandboxSession: + async def resume( + self, + state: SandboxSessionState, + *, + codex: bool | CodexConfig = False, + ) -> SandboxSession: if not isinstance(state, ModalSandboxSessionState): raise TypeError("ModalSandboxClient.resume expects a ModalSandboxSessionState") - inner = ModalSandboxSession.from_state(state) + inner = ModalSandboxSession.from_state(apply_codex_to_session_state(state, codex)) return self._wrap_session(inner, instrumentation=self._instrumentation) def deserialize_session_state(self, payload: dict[str, object]) -> SandboxSessionState: diff --git a/src/agents/sandbox/__init__.py b/src/agents/sandbox/__init__.py index 38a6dd8abb..6a02392b43 100644 --- a/src/agents/sandbox/__init__.py +++ b/src/agents/sandbox/__init__.py @@ -2,6 +2,7 @@ from ..run_config import SandboxRunConfig from .capabilities import Capability +from .codex_config import CodexConfig from .entries import Dir, LocalFile from .errors import ( ErrorCode, @@ -25,6 +26,7 @@ __all__ = [ "Capability", + "CodexConfig", "Dir", "ErrorCode", "ExecResult", diff --git a/src/agents/sandbox/codex_config.py b/src/agents/sandbox/codex_config.py new file mode 100644 index 0000000000..77322a062b --- /dev/null +++ b/src/agents/sandbox/codex_config.py @@ -0,0 +1,96 @@ +from __future__ import annotations + +from dataclasses import dataclass +from pathlib import Path +from typing import TypeVar + +from .entries import Codex, resolve_workspace_path +from .errors import InvalidManifestPathError +from .manifest import Manifest +from .session.sandbox_session_state import SandboxSessionState + +DEFAULT_CODEX_PATH = "~/.codex/codex" +# TODO: this should eventually be sourced from the pinned version of codex-python-sdk +DEFAULT_CODEX_VERSION = "0.114.0" +SandboxSessionStateT = TypeVar("SandboxSessionStateT", bound=SandboxSessionState) + + +@dataclass(frozen=True) +class CodexConfig: + path: str | Path = DEFAULT_CODEX_PATH + version: str = DEFAULT_CODEX_VERSION + + +def normalize_codex_config(codex: bool | CodexConfig) -> CodexConfig | None: + if isinstance(codex, CodexConfig): + return codex + if codex: + return CodexConfig() + return None + + +def apply_codex_to_manifest( + manifest: Manifest | None, + codex: bool | CodexConfig, +) -> Manifest: + normalized = normalize_codex_config(codex) + base_manifest = manifest.model_copy(deep=True) if manifest is not None else Manifest() + if normalized is None: + return base_manifest + + codex_path = _manifest_codex_path(manifest=base_manifest, configured_path=normalized.path) + entries = dict(base_manifest.entries) + entries.setdefault(codex_path, Codex(version=normalized.version)) + return base_manifest.model_copy(update={"entries": entries}) + + +def manifest_has_codex_entry( + manifest: Manifest | None, + codex: bool | CodexConfig, +) -> bool: + normalized = normalize_codex_config(codex) + if normalized is None or manifest is None: + return normalized is None + + codex_path = _manifest_codex_path(manifest=manifest, configured_path=normalized.path) + return codex_path in {manifest._coerce_rel_path(path) for path in manifest.entries} + + +def apply_codex_to_session_state( + state: SandboxSessionStateT, + codex: bool | CodexConfig, +) -> SandboxSessionStateT: + return state.model_copy(update={"manifest": apply_codex_to_manifest(state.manifest, codex)}) + + +def _manifest_codex_path(*, manifest: Manifest, configured_path: str | Path) -> Path: + configured_str = str(configured_path) + if configured_str == "~": + return Path(".") + if configured_str.startswith("~/"): + home_relative = Path(configured_str.removeprefix("~/")) + manifest._validate_rel_path(home_relative) + return home_relative + + raw_path = Path(configured_path) + if not raw_path.is_absolute(): + manifest._validate_rel_path(raw_path) + return raw_path + + candidate_roots = [Path(manifest.root)] + default_root = Path(str(Manifest.model_fields["root"].default)) + if default_root not in candidate_roots: + candidate_roots.append(default_root) + + for root in candidate_roots: + try: + resolved = resolve_workspace_path( + root, + raw_path, + allow_absolute_within_root=True, + ) + except InvalidManifestPathError: + continue + return resolved.relative_to(root) + + raise InvalidManifestPathError(rel=raw_path, reason="absolute") diff --git a/src/agents/sandbox/entries/__init__.py b/src/agents/sandbox/entries/__init__.py index 6c77820752..fd663e95a0 100644 --- a/src/agents/sandbox/entries/__init__.py +++ b/src/agents/sandbox/entries/__init__.py @@ -2,6 +2,7 @@ from .artifacts import Dir, File, GitRepo, LocalDir, LocalFile from .base import BaseEntry, resolve_workspace_path +from .codex import Codex from .mounts import ( AzureBlobMount, FuseMountPattern, @@ -17,6 +18,7 @@ __all__ = [ "AzureBlobMount", "BaseEntry", + "Codex", "Dir", "File", "FuseMountPattern", diff --git a/src/agents/sandbox/entries/codex.py b/src/agents/sandbox/entries/codex.py new file mode 100644 index 0000000000..e59f14f7c8 --- /dev/null +++ b/src/agents/sandbox/entries/codex.py @@ -0,0 +1,268 @@ +from __future__ import annotations + +import uuid +from pathlib import Path +from typing import TYPE_CHECKING, Literal + +import httpx + +from ..errors import UnsupportedCodexTargetError +from ..materialization import MaterializedFile +from ..util.iterator_io import IteratorIO +from .base import BaseEntry + +if TYPE_CHECKING: + from ..session.base_sandbox_session import BaseSandboxSession + +_SUPPORTED_CODEX_OPERATING_SYSTEMS = ("linux", "darwin", "windows") +_SUPPORTED_CODEX_ARCHITECTURES = ("x86_64", "aarch64") +_SUPPORTED_CODEX_LINUX_LIBC_VARIANTS = ("gnu", "musl") +_CODEX_ARCH_ALIASES = { + "x86_64": "x86_64", + "amd64": "x86_64", + "aarch64": "aarch64", + "arm64": "aarch64", +} + + +class Codex(BaseEntry): + type: Literal["codex"] = "codex" + version: str = "latest" + + async def apply( + self, + session: BaseSandboxSession, + dest: Path, + base_dir: Path, + ) -> list[MaterializedFile]: + _ = base_dir + asset_name = await session.resolve_codex_github_asset_name() + if asset_name.endswith(".exe.tar.gz"): + raise RuntimeError("Windows Codex artifacts are not supported in sandbox manifests.") + archive_url = self._release_asset_url(asset_name) + staging_dir = dest.parent / f".codex-install-{uuid.uuid4().hex}" + archive_path = staging_dir / asset_name + + await session.mkdir(dest.parent, parents=True) + await session.mkdir(staging_dir, parents=True) + try: + with _stream_release_asset(archive_url) as response: + response.raise_for_status() + await session.write( + archive_path, + _IteratorStreamWithLength( + response.iter_bytes(), + content_length=_parse_content_length(response), + ), + ) + + extract_result = await session.exec( + "tar", + "-xzf", + archive_path, + "-C", + staging_dir, + shell=False, + ) + if not extract_result.ok(): + raise RuntimeError(extract_result.stderr.decode("utf-8", errors="replace")) + + extracted_binary = await self._resolve_extracted_binary_path( + session=session, + staging_dir=staging_dir, + ) + await self._copy_extracted_binary_to_destination( + session=session, + extracted_binary=extracted_binary, + dest=dest, + ) + finally: + await session.rm(staging_dir, recursive=True) + + await self._apply_metadata(session, dest) + return [] + + def _release_asset_url(self, asset_name: str) -> str: + if self.version == "latest": + return f"https://github.com/openai/codex/releases/latest/download/{asset_name}" + return ( + f"https://github.com/openai/codex/releases/download/rust-v{self.version}/{asset_name}" + ) + + async def _resolve_extracted_binary_path( + self, + *, + session: BaseSandboxSession, + staging_dir: Path, + ) -> str: + result = await session.exec( + f"find {staging_dir} -type f \\( -name codex -o -name 'codex-*' \\) | head -n 1" + ) + if not result.ok(): + raise RuntimeError("Codex binary not found in extracted archive.") + path = result.stdout.decode("utf-8", errors="replace").strip() + if not path: + raise RuntimeError("Codex binary not found in extracted archive.") + return path + + async def _copy_extracted_binary_to_destination( + self, + *, + session: BaseSandboxSession, + extracted_binary: str, + dest: Path, + ) -> None: + result = await session.exec("cp", extracted_binary, dest, shell=False) + if not result.ok(): + raise RuntimeError(result.stderr.decode("utf-8", errors="replace")) + + +async def resolve_codex_github_asset_name(*, session: BaseSandboxSession) -> str: + """Resolve the Codex GitHub release asset filename for the session target.""" + + target_triple = await resolve_codex_target_triple(session=session) + suffix = ".exe.tar.gz" if target_triple.endswith("windows-msvc") else ".tar.gz" + return f"codex-{target_triple}{suffix}" + + +async def resolve_codex_target_triple(*, session: BaseSandboxSession) -> str: + """Resolve the Codex release target triple for the session target platform.""" + + target_os = await _detect_target_os(session=session) + target_arch = await _detect_target_arch(session=session, target_os=target_os) + + if target_os == "linux": + libc = await _detect_linux_libc_variant(session=session) + return resolve_codex_target_triple_for_target( + target_os=target_os, + target_arch=target_arch, + linux_libc=libc, + ) + + return resolve_codex_target_triple_for_target( + target_os=target_os, + target_arch=target_arch, + ) + + +def resolve_codex_target_triple_for_target( + *, + target_os: str, + target_arch: str, + linux_libc: str | None = None, +) -> str: + normalized_os = target_os.strip().lower() + normalized_arch = target_arch.strip().lower() + canonical_arch = _CODEX_ARCH_ALIASES.get(normalized_arch) + + if normalized_os == "linux": + if canonical_arch is not None: + libc = linux_libc or "gnu" + if libc not in _SUPPORTED_CODEX_LINUX_LIBC_VARIANTS: + raise UnsupportedCodexTargetError( + reason="linux_libc", + target_os=target_os, + target_arch=target_arch, + linux_libc=linux_libc, + supported_operating_systems=_SUPPORTED_CODEX_OPERATING_SYSTEMS, + supported_architectures=_SUPPORTED_CODEX_ARCHITECTURES, + supported_linux_libc_variants=_SUPPORTED_CODEX_LINUX_LIBC_VARIANTS, + ) + return f"{canonical_arch}-unknown-linux-{libc}" + elif normalized_os == "darwin": + if canonical_arch is not None: + return f"{canonical_arch}-apple-darwin" + elif normalized_os == "windows": + if canonical_arch is not None: + return f"{canonical_arch}-pc-windows-msvc" + else: + raise UnsupportedCodexTargetError( + reason="operating_system", + target_os=target_os, + target_arch=target_arch, + supported_operating_systems=_SUPPORTED_CODEX_OPERATING_SYSTEMS, + supported_architectures=_SUPPORTED_CODEX_ARCHITECTURES, + ) + + raise UnsupportedCodexTargetError( + reason="architecture", + target_os=normalized_os, + target_arch=target_arch, + supported_operating_systems=_SUPPORTED_CODEX_OPERATING_SYSTEMS, + supported_architectures=_SUPPORTED_CODEX_ARCHITECTURES, + ) + + +async def _detect_target_os( + *, + session: BaseSandboxSession, +) -> Literal["linux", "darwin", "windows"]: + unix_result = await session.exec("uname", "-s", shell=False) + if unix_result.ok(): + system = unix_result.stdout.decode("utf-8", errors="replace").strip().lower() + if system == "linux": + return "linux" + if system == "darwin": + return "darwin" + + windows_result = await session.exec("cmd", "/c", "echo", "%OS%", shell=False) + if windows_result.ok(): + system = windows_result.stdout.decode("utf-8", errors="replace").strip().lower() + if system == "windows_nt": + return "windows" + + raise RuntimeError("Unable to detect sandbox target operating system.") + + +async def _detect_target_arch(*, session: BaseSandboxSession, target_os: str) -> str: + if target_os == "windows": + result = await session.exec( + "cmd", + "/c", + "echo", + "%PROCESSOR_ARCHITECTURE%", + shell=False, + ) + else: + result = await session.exec("uname", "-m", shell=False) + + if result.ok(): + return result.stdout.decode("utf-8", errors="replace").strip().lower() + + raise RuntimeError(f"Unable to detect sandbox target architecture for {target_os}.") + + +async def _detect_linux_libc_variant(*, session: BaseSandboxSession) -> Literal["gnu", "musl"]: + result = await session.exec("getconf", "GNU_LIBC_VERSION", shell=False) + if result.ok(): + return "gnu" + + result = await session.exec("ldd", "--version", shell=False) + combined = (result.stdout + result.stderr).decode("utf-8", errors="replace").lower() + if "musl" in combined: + return "musl" + if result.ok() and combined: + return "gnu" + + raise RuntimeError("Unable to detect Linux libc variant for Codex release asset.") + + +class _IteratorStreamWithLength(IteratorIO): + def __init__(self, it, *, content_length: int | None) -> None: + super().__init__(it=it) + self.content_length = content_length + + +def _stream_release_asset(url: str): + return httpx.stream("GET", url, follow_redirects=True) + + +def _parse_content_length(response: httpx.Response) -> int | None: + value = response.headers.get("Content-Length") + if value is None: + return None + try: + parsed = int(value) + except ValueError: + return None + return parsed if parsed >= 0 else None diff --git a/src/agents/sandbox/errors.py b/src/agents/sandbox/errors.py index b2884d8b3e..445625095f 100644 --- a/src/agents/sandbox/errors.py +++ b/src/agents/sandbox/errors.py @@ -36,6 +36,7 @@ def __str__(self) -> str: GIT_MISSING_IN_IMAGE = "git_missing_in_image" GIT_CLONE_ERROR = "git_clone_error" GIT_COPY_ERROR = "git_copy_error" + UNSUPPORTED_CODEX_TARGET = "unsupported_codex_target" MOUNT_MISSING_TOOL = "mount_missing_tool" MOUNT_FAILED = "mount_failed" @@ -116,6 +117,75 @@ def _format_command(command: Sequence[str | Path]) -> str: return " ".join(str(p) for p in command) +class UnsupportedCodexTargetError(ArtifactError): + """Codex release assets do not support the detected target platform.""" + + reason: Literal["operating_system", "architecture", "linux_libc"] + target_os: str + target_arch: str + linux_libc: str | None + supported_operating_systems: tuple[str, ...] + supported_architectures: tuple[str, ...] + supported_linux_libc_variants: tuple[str, ...] + + def __init__( + self, + *, + reason: Literal["operating_system", "architecture", "linux_libc"], + target_os: str, + target_arch: str, + linux_libc: str | None = None, + supported_operating_systems: Sequence[str], + supported_architectures: Sequence[str], + supported_linux_libc_variants: Sequence[str] = (), + context: Mapping[str, object] | None = None, + cause: BaseException | None = None, + ) -> None: + supported_os = tuple(supported_operating_systems) + supported_arches = tuple(supported_architectures) + supported_libc_variants = tuple(supported_linux_libc_variants) + + if reason == "operating_system": + message = ( + f"Unsupported Codex target operating system: {target_os}. " + f"Available operating systems: {', '.join(supported_os)}." + ) + elif reason == "architecture": + message = ( + f"Unsupported Codex target architecture for {target_os}: {target_arch}. " + f"Available architectures: {', '.join(supported_arches)}." + ) + else: + message = ( + "Unsupported Linux libc variant for Codex target resolution: " + f"{linux_libc}. Available libc variants: {', '.join(supported_libc_variants)}." + ) + + super().__init__( + message=message, + error_code=ErrorCode.UNSUPPORTED_CODEX_TARGET, + op="materialize", + context={ + "reason": reason, + "target_os": target_os, + "target_arch": target_arch, + "linux_libc": linux_libc, + "supported_operating_systems": supported_os, + "supported_architectures": supported_arches, + "supported_linux_libc_variants": supported_libc_variants, + **_as_context(context), + }, + cause=cause, + ) + self.reason = reason + self.target_os = target_os + self.target_arch = target_arch + self.linux_libc = linux_libc + self.supported_operating_systems = supported_os + self.supported_architectures = supported_arches + self.supported_linux_libc_variants = supported_libc_variants + + class InvalidManifestPathError(ConfigurationError): """Manifest path was invalid (absolute or escaped the workspace root).""" diff --git a/src/agents/sandbox/runtime_session_manager.py b/src/agents/sandbox/runtime_session_manager.py index 0ee112e8dd..50a100c1b5 100644 --- a/src/agents/sandbox/runtime_session_manager.py +++ b/src/agents/sandbox/runtime_session_manager.py @@ -7,6 +7,7 @@ from typing import Any, Generic, cast from ..agent import Agent +from ..exceptions import UserError from ..run_config import SandboxRunConfig from ..run_context import TContext from ..run_state import ( @@ -15,6 +16,7 @@ _build_agent_identity_keys_by_id, ) from .capabilities import Capability +from .codex_config import manifest_has_codex_entry from .manifest import Manifest from .sandbox_agent import SandboxAgent from .session.base_sandbox_session import BaseSandboxSession @@ -253,6 +255,7 @@ async def _create_resources( ) -> _SandboxSessionResources: sandbox_config = self._require_sandbox_config() if sandbox_config.session is not None: + self._validate_injected_session(agent=agent, session=sandbox_config.session) return _SandboxSessionResources( session=sandbox_config.session, client=None, @@ -277,7 +280,7 @@ async def _create_resources( session_state=explicit_state, ) return _SandboxSessionResources( - session=await client.resume(explicit_state), + session=await client.resume(explicit_state, codex=agent.codex), client=client, owns_session=True, ) @@ -298,6 +301,7 @@ async def _create_resources( session = await client.create( snapshot=self._resolve_snapshot_spec(sandbox_config.snapshot), manifest=effective_manifest, + codex=agent.codex, options=options, ) return _SandboxSessionResources(session=session, client=client, owns_session=True) @@ -424,6 +428,23 @@ def _process_resumed_state_manifest( return session_state return session_state.model_copy(update={"manifest": processed_manifest}) + @staticmethod + def _validate_injected_session( + *, + agent: SandboxAgent[TContext], + session: BaseSandboxSession, + ) -> None: + if manifest_has_codex_entry(session.state.manifest, agent.codex): + return + if not agent.codex: + return + raise UserError( + "Injected sandbox sessions are used as-is and are not auto-provisioned with Codex. " + f"Session for SandboxAgent {agent.name!r} is missing Codex. " + "Create the session with `client.create(..., codex=True)` or set `codex=False` " + "on the SandboxAgent." + ) + def _release_agents(self) -> None: if not self._acquired_agents: return diff --git a/src/agents/sandbox/sandbox_agent.py b/src/agents/sandbox/sandbox_agent.py index 122c5b7568..d2d7ab21f8 100644 --- a/src/agents/sandbox/sandbox_agent.py +++ b/src/agents/sandbox/sandbox_agent.py @@ -5,6 +5,7 @@ from ..agent import Agent from ..run_context import TContext from .capabilities import Capability +from .codex_config import CodexConfig from .manifest import Manifest @@ -25,4 +26,7 @@ class SandboxAgent(Agent[TContext]): capabilities: list[Capability] = field(default_factory=list) """Sandbox capabilities that can mutate the manifest, add instructions, and expose tools.""" + codex: bool | CodexConfig = True + """Whether to provision Codex for runtime-created or resumed sandbox sessions.""" + _sandbox_concurrency_guard: object | None = field(default=None, init=False, repr=False) diff --git a/src/agents/sandbox/sandboxes/docker.py b/src/agents/sandbox/sandboxes/docker.py index 6e9128c6bf..45707b04a4 100644 --- a/src/agents/sandbox/sandboxes/docker.py +++ b/src/agents/sandbox/sandboxes/docker.py @@ -1,7 +1,9 @@ import asyncio import io +import queue import tarfile import tempfile +import threading import uuid from concurrent.futures import ThreadPoolExecutor from dataclasses import dataclass @@ -12,7 +14,9 @@ from docker import DockerClient as DockerSDKClient from docker.models.containers import Container # type: ignore[import-untyped] from docker.utils import parse_repository_tag # type: ignore[import-untyped] +from typing_extensions import Buffer +from ..codex_config import CodexConfig, apply_codex_to_manifest, apply_codex_to_session_state from ..entries import ( FuseMountPattern, Mount, @@ -50,6 +54,54 @@ ) +class _QueueWriter(io.RawIOBase): + def __init__(self, chunks: queue.Queue[bytes | BaseException | None]) -> None: + self._chunks = chunks + self._closed = False + + def writable(self) -> bool: + return True + + def write(self, b: Buffer, /) -> int: + if self._closed: + raise ValueError("I/O operation on closed file.") + payload = bytes(b) + if payload: + self._chunks.put(payload) + return len(payload) + + def close(self) -> None: + if self._closed: + return + self._closed = True + super().close() + + +class _StreamingTarPipe: + def __init__(self) -> None: + self._chunks: queue.Queue[bytes | BaseException | None] = queue.Queue() + self.writer = _QueueWriter(self._chunks) + self.reader = IteratorIO(self._iter_chunks()) + + def _iter_chunks(self): + while True: + item = self._chunks.get() + if item is None: + return + if isinstance(item, BaseException): + raise item + yield item + + def set_error(self, error: BaseException) -> None: + self._chunks.put(error) + + def close(self) -> None: + try: + self.writer.close() + finally: + self._chunks.put(None) + + class DockerSandboxSessionState(SandboxSessionState): image: str container_id: str @@ -309,7 +361,6 @@ async def read(self, path: Path) -> io.IOBase: raise WorkspaceArchiveReadError(path=path, cause=e) from e async def write(self, path: Path, data: io.IOBase) -> None: - # Buffer the file first so we can set TarInfo.size correctly. payload = coerce_write_payload(path=path, data=data) path = resolve_workspace_path( @@ -336,18 +387,27 @@ async def write(self, path: Path, data: io.IOBase) -> None: error_path=self._ARCHIVE_STAGING_DIR, ) - info = tarfile.TarInfo(name=staging_name) - info.size = len(payload) - - tar_buf = io.BytesIO() - with tarfile.open(fileobj=tar_buf, mode="w") as tar: - tar.addfile(info, io.BytesIO(bytes(payload))) - - tar_buf.seek(0) try: - self._container.put_archive(str(self._ARCHIVE_STAGING_DIR), tar_buf) + tar_stream: io.IOBase + if payload.content_length is None: + tar_stream = self._buffered_single_file_tar( + staging_name=staging_name, + stream=payload.stream, + ) + else: + tar_stream = self._streaming_single_file_tar( + staging_name=staging_name, + stream=payload.stream, + content_length=payload.content_length, + ) + ok = self._container.put_archive(str(self._ARCHIVE_STAGING_DIR), tar_stream) except docker.errors.APIError as e: raise WorkspaceArchiveWriteError(path=self._ARCHIVE_STAGING_DIR, cause=e) from e + if not ok: + raise WorkspaceArchiveWriteError( + path=self._ARCHIVE_STAGING_DIR, + context={"reason": "put_archive_returned_false"}, + ) # Copy into place using a process inside the container, which can see mounts. cp_res = await self.exec("cp", "--", str(staging_path), str(path), shell=False) @@ -364,6 +424,50 @@ async def write(self, path: Path, data: io.IOBase) -> None: # Best-effort cleanup. Ignore failures (e.g. concurrent cleanup). await self._rm_best_effort(staging_path) + @staticmethod + def _buffered_single_file_tar(*, staging_name: str, stream: io.IOBase) -> io.BytesIO: + payload = stream.read() + if isinstance(payload, bytearray): + payload = bytes(payload) + if not isinstance(payload, bytes): + raise TypeError(f"expected bytes payload, got {type(payload).__name__}") + + info = tarfile.TarInfo(name=staging_name) + info.size = len(payload) + + tar_buf = io.BytesIO() + with tarfile.open(fileobj=tar_buf, mode="w") as tar: + tar.addfile(info, io.BytesIO(payload)) + tar_buf.seek(0) + return tar_buf + + @staticmethod + def _streaming_single_file_tar( + *, + staging_name: str, + stream: io.IOBase, + content_length: int, + ) -> io.IOBase: + pipe = _StreamingTarPipe() + + def _produce() -> None: + info = tarfile.TarInfo(name=staging_name) + info.size = content_length + try: + with tarfile.open(fileobj=pipe.writer, mode="w|") as tar: + tar.addfile(info, stream) + except BaseException as exc: + pipe.set_error(exc) + finally: + pipe.close() + + threading.Thread( + target=_produce, + name=f"docker-put-archive-{staging_name}", + daemon=True, + ).start() + return pipe.reader + async def running(self) -> bool: # docker-py caches container attributes; refresh to avoid stale status, # especially right after start/stop. @@ -541,8 +645,10 @@ async def create( *, snapshot: SnapshotSpec | None = None, manifest: Manifest | None = None, + codex: bool | CodexConfig = False, options: DockerSandboxClientOptions, ) -> SandboxSession: + manifest = apply_codex_to_manifest(manifest, codex) image = options.image container = await self._create_container(image, manifest=manifest) @@ -587,9 +693,15 @@ async def delete(self, session: SandboxSession) -> SandboxSession: return session return session - async def resume(self, state: SandboxSessionState) -> SandboxSession: + async def resume( + self, + state: SandboxSessionState, + *, + codex: bool | CodexConfig = False, + ) -> SandboxSession: if not isinstance(state, DockerSandboxSessionState): raise TypeError("DockerSandboxClient.resume expects a DockerSandboxSessionState") + state = apply_codex_to_session_state(state, codex) container = self.get_container(state.container_id) reused_existing_container = container is not None if container is None: diff --git a/src/agents/sandbox/sandboxes/unix_local.py b/src/agents/sandbox/sandboxes/unix_local.py index 04f9e7e55a..5fd2d70a85 100644 --- a/src/agents/sandbox/sandboxes/unix_local.py +++ b/src/agents/sandbox/sandboxes/unix_local.py @@ -13,6 +13,7 @@ from pathlib import Path from typing import Literal, cast +from ..codex_config import CodexConfig, apply_codex_to_manifest, apply_codex_to_session_state from ..entries import resolve_workspace_path from ..errors import ( ExecNonZeroError, @@ -425,11 +426,31 @@ async def ls(self, path: Path | str) -> list[FileEntry]: async def mkdir(self, path: Path | str, *, parents: bool = False) -> None: normalized = self.normalize_path(path) - workspace_root = Path(self.state.manifest.root).resolve() - if parents and normalized == workspace_root: - workspace_root.mkdir(parents=True, exist_ok=True) - return - await super().mkdir(normalized, parents=parents) + try: + normalized.mkdir(parents=parents, exist_ok=True) + except OSError as e: + raise WorkspaceArchiveWriteError(path=normalized, cause=e) from e + + async def rm(self, path: Path | str, *, recursive: bool = False) -> None: + normalized = self.normalize_path(path) + try: + if normalized.is_dir() and not normalized.is_symlink(): + if recursive: + shutil.rmtree(normalized) + else: + normalized.rmdir() + else: + normalized.unlink() + except FileNotFoundError as e: + if recursive: + return + raise ExecNonZeroError( + ExecResult(stdout=b"", stderr=str(e).encode("utf-8"), exit_code=1), + command=("rm", "-rf" if recursive else "--", str(normalized)), + cause=e, + ) from e + except OSError as e: + raise WorkspaceArchiveWriteError(path=normalized, cause=e) from e async def read(self, path: Path) -> io.IOBase: workspace_path = self._resolve_workspace_path(path) @@ -446,7 +467,8 @@ async def write(self, path: Path, data: io.IOBase) -> None: workspace_path = self._resolve_workspace_path(path) try: workspace_path.parent.mkdir(parents=True, exist_ok=True) - workspace_path.write_bytes(payload) + with workspace_path.open("wb") as f: + shutil.copyfileobj(payload.stream, f) except OSError as e: raise WorkspaceArchiveWriteError(path=workspace_path, cause=e) from e @@ -516,10 +538,12 @@ async def create( *, snapshot: SnapshotSpec | None = None, manifest: Manifest | None = None, + codex: bool | CodexConfig = False, options: None = None, ) -> SandboxSession: if options is not None: raise ValueError("UnixLocalSandboxClient.create does not accept options") + manifest = apply_codex_to_manifest(manifest, codex) # For local execution, runner-created sessions should always get an isolated temp root # unless the caller explicitly chose a custom host path. workspace_root_owned = False @@ -558,10 +582,15 @@ async def delete(self, session: SandboxSession) -> SandboxSession: pass return session - async def resume(self, state: SandboxSessionState) -> SandboxSession: + async def resume( + self, + state: SandboxSessionState, + *, + codex: bool | CodexConfig = False, + ) -> SandboxSession: if not isinstance(state, UnixLocalSandboxSessionState): raise TypeError("UnixLocalSandboxClient.resume expects a UnixLocalSandboxSessionState") - inner = UnixLocalSandboxSession.from_state(state) + inner = UnixLocalSandboxSession.from_state(apply_codex_to_session_state(state, codex)) return self._wrap_session(inner, instrumentation=self._instrumentation) def deserialize_session_state(self, payload: dict[str, object]) -> SandboxSessionState: diff --git a/src/agents/sandbox/session/base_sandbox_session.py b/src/agents/sandbox/session/base_sandbox_session.py index 839d4fb5e3..e6eeb808ea 100644 --- a/src/agents/sandbox/session/base_sandbox_session.py +++ b/src/agents/sandbox/session/base_sandbox_session.py @@ -10,11 +10,17 @@ from typing_extensions import Self from ..entries import BaseEntry, resolve_workspace_path +from ..entries.codex import ( + Codex, + resolve_codex_github_asset_name as resolve_codex_github_asset_name_for_session, + resolve_codex_target_triple as resolve_codex_target_triple_for_session, +) from ..errors import ( ExecNonZeroError, InvalidCompressionSchemeError, ) from ..files import FileEntry +from ..manifest import Manifest from ..materialization import MaterializationResult, MaterializedFile from ..snapshot import NoopSnapshot from ..types import ExecResult, User @@ -44,6 +50,7 @@ async def start(self) -> None: # Reapply only ephemeral manifest entries on resume so persisted workspace state wins # for durable files while temporary scaffolding is rebuilt for the new process. await self.apply_manifest(only_ephemeral=True) + await self._materialize_missing_codex_entries_on_resume() else: await self.apply_manifest() @@ -139,6 +146,16 @@ async def exec( sanitized_command = self._prepare_exec_command(*command, shell=shell, user=user) return await self._exec_internal(*sanitized_command, timeout=timeout) + async def resolve_codex_github_asset_name(self) -> str: + """Resolve the Codex GitHub release asset filename for the session target.""" + + return await resolve_codex_github_asset_name_for_session(session=self) + + async def resolve_codex_target_triple(self) -> str: + """Resolve the Codex release target triple for the session target platform.""" + + return await resolve_codex_target_triple_for_session(session=self) + def _prepare_exec_command( self, *command: str | Path, @@ -326,6 +343,29 @@ def normalize_path(self, path: Path | str) -> Path: def describe(self) -> str: return self.state.manifest.describe() + async def _materialize_missing_codex_entries_on_resume(self) -> None: + missing_codex_entries: dict[str | Path, BaseEntry] = {} + for rel_path, artifact in self.state.manifest.iter_entries(): + if not isinstance(artifact, Codex): + continue + exists = await self.exec("test", "-e", str(self.normalize_path(rel_path)), shell=False) + if exists.ok(): + continue + missing_codex_entries[rel_path] = artifact.model_copy(deep=True) + + if not missing_codex_entries: + return + + codex_manifest = Manifest( + root=self.state.manifest.root, + entries=missing_codex_entries, + ) + await ManifestApplier( + mkdir=lambda path: self.mkdir(path, parents=True), + exec_checked_nonzero=self._exec_checked_nonzero, + apply_entry=lambda artifact, dest, base_dir: artifact.apply(self, dest, base_dir), + ).apply_manifest(codex_manifest, base_dir=self._manifest_base_dir()) + async def _extract_tar_archive( self, *, diff --git a/src/agents/sandbox/session/sandbox_client.py b/src/agents/sandbox/session/sandbox_client.py index 3fe84b13b3..25ee8af777 100644 --- a/src/agents/sandbox/session/sandbox_client.py +++ b/src/agents/sandbox/session/sandbox_client.py @@ -3,6 +3,7 @@ import abc from typing import Generic, TypeVar +from ..codex_config import CodexConfig from ..manifest import Manifest from ..snapshot import SnapshotSpec from .base_sandbox_session import BaseSandboxSession @@ -46,6 +47,7 @@ async def create( *, snapshot: SnapshotSpec | None = None, manifest: Manifest | None = None, + codex: bool | CodexConfig = False, options: ClientOptionsT, ) -> SandboxSession: """Create a new session. @@ -55,6 +57,8 @@ async def create( the session. If omitted, the session uses a no-op snapshot. manifest: Optional manifest to materialize into the workspace when the session starts. + codex: Whether to provision Codex into the workspace, or a custom + Codex provisioning config. options: Sandbox-specific settings. For example, Docker expects ``DockerSandboxClientOptions(image="...")``. Returns: @@ -70,6 +74,8 @@ async def delete(self, session: SandboxSession) -> SandboxSession: async def resume( self, state: SandboxSessionState, + *, + codex: bool | CodexConfig = False, ) -> SandboxSession: """Resume a session from a previously persisted `SandboxSessionState`. diff --git a/src/agents/sandbox/session/workspace_payloads.py b/src/agents/sandbox/session/workspace_payloads.py index c95dbf636c..5141707861 100644 --- a/src/agents/sandbox/session/workspace_payloads.py +++ b/src/agents/sandbox/session/workspace_payloads.py @@ -1,15 +1,79 @@ from __future__ import annotations import io +from dataclasses import dataclass from pathlib import Path from ..errors import WorkspaceWriteTypeError -def coerce_write_payload(*, path: Path, data: io.IOBase) -> bytes: - payload = data.read() - if isinstance(payload, str): - payload = payload.encode("utf-8") - if not isinstance(payload, (bytes, bytearray)): - raise WorkspaceWriteTypeError(path=path, actual_type=type(payload).__name__) - return bytes(payload) +@dataclass(frozen=True) +class WritePayload: + stream: io.IOBase + content_length: int | None = None + + +class _BinaryReadAdapter(io.IOBase): + def __init__(self, *, path: Path, stream: io.IOBase) -> None: + self._path = path + self._stream = stream + + def readable(self) -> bool: + return True + + def read(self, size: int = -1) -> bytes: + chunk = self._stream.read(size) + if chunk is None: + return b"" + if isinstance(chunk, bytes): + return chunk + if isinstance(chunk, bytearray): + return bytes(chunk) + raise WorkspaceWriteTypeError(path=self._path, actual_type=type(chunk).__name__) + + def readinto(self, b: bytearray) -> int: + data = self.read(len(b)) + n = len(data) + b[:n] = data + return n + + def seek(self, offset: int, whence: int = io.SEEK_SET) -> int: + return int(self._stream.seek(offset, whence)) + + def tell(self) -> int: + return int(self._stream.tell()) + + +def coerce_write_payload(*, path: Path, data: io.IOBase) -> WritePayload: + stream = _BinaryReadAdapter(path=path, stream=data) + return WritePayload(stream=stream, content_length=_best_effort_content_length(data)) + + +def _best_effort_content_length(stream: io.IOBase) -> int | None: + for attr in ("content_length", "length"): + value = getattr(stream, attr, None) + if isinstance(value, int) and value >= 0: + return value + + headers = getattr(stream, "headers", None) + if headers is not None: + content_length = None + get = getattr(headers, "get", None) + if callable(get): + content_length = get("Content-Length") + if isinstance(content_length, str): + try: + parsed = int(content_length) + except ValueError: + parsed = None + if parsed is not None and parsed >= 0: + return parsed + + try: + pos = stream.tell() + stream.seek(0, io.SEEK_END) + end = stream.tell() + stream.seek(pos, io.SEEK_SET) + return int(end - pos) + except Exception: + return None diff --git a/tests/extensions/test_sandbox_e2b.py b/tests/extensions/test_sandbox_e2b.py index 83ba1be614..f74af4eed1 100644 --- a/tests/extensions/test_sandbox_e2b.py +++ b/tests/extensions/test_sandbox_e2b.py @@ -28,6 +28,9 @@ def __init__(self, *, stdout: str = "", stderr: str = "", exit_code: int = 0) -> class _FakeE2BFiles: + def __init__(self) -> None: + self.make_dir_calls: list[tuple[str, float | None]] = [] + def write( self, path: str, @@ -39,6 +42,10 @@ def write( def remove(self, path: str, request_timeout: float | None = None) -> None: _ = (path, request_timeout) + def make_dir(self, path: str, request_timeout: float | None = None) -> bool: + self.make_dir_calls.append((path, request_timeout)) + return True + def read(self, path: str, format: str = "bytes") -> bytes: _ = (path, format) return b"" @@ -206,6 +213,7 @@ async def test_e2b_start_prepares_workspace_root_for_command_cwd() -> None: assert result.ok() assert session.state.workspace_root_ready is True assert session._workspace_root_ready is True # noqa: SLF001 + assert sandbox.files.make_dir_calls == [("/workspace", 10), ("/workspace", 10)] assert sandbox.commands.calls == [ { "command": "mkdir -p -- /workspace", diff --git a/tests/test_sandbox_entries.py b/tests/test_sandbox_entries.py index b636c0eda9..2defb52f67 100644 --- a/tests/test_sandbox_entries.py +++ b/tests/test_sandbox_entries.py @@ -1,16 +1,19 @@ from __future__ import annotations import io +import tarfile from pathlib import Path +from typing import Literal import pytest -from agents.sandbox.entries import Dir, File, GitRepo, LocalFile +import agents.sandbox.entries.codex as codex_module +from agents.sandbox.entries import Codex, Dir, File, GitRepo, LocalFile from agents.sandbox.errors import ExecNonZeroError from agents.sandbox.manifest import Manifest from agents.sandbox.session.base_sandbox_session import BaseSandboxSession from agents.sandbox.session.sandbox_session_state import SandboxSessionState -from agents.sandbox.snapshot import NoopSnapshot +from agents.sandbox.snapshot import NoopSnapshot, SnapshotBase from agents.sandbox.types import ExecResult, User @@ -91,6 +94,93 @@ async def _exec_internal( return ExecResult(stdout=b"", stderr=b"", exit_code=0) +class _CodexSession(_RecordingSession): + def __init__(self, asset_name: str, *, resolved_binary_path: str) -> None: + super().__init__() + self.asset_name = asset_name + self.resolved_binary_path = resolved_binary_path + + async def _exec_internal( + self, + *command: str | Path, + timeout: float | None = None, + ) -> ExecResult: + _ = timeout + cmd = tuple(str(part) for part in command) + self.exec_calls.append(cmd) + if cmd[:2] == ("sh", "-lc") and "find " in cmd[2] and "head -n 1" in cmd[2]: + return ExecResult( + stdout=f"{self.resolved_binary_path}\n".encode(), + stderr=b"", + exit_code=0, + ) + return ExecResult(stdout=b"", stderr=b"", exit_code=0) + + async def resolve_codex_github_asset_name(self) -> str: + return self.asset_name + + +class _RestorableSnapshot(SnapshotBase): + __test__ = False + type: Literal["entry-restorable"] = "entry-restorable" + + async def persist(self, data: io.IOBase) -> None: + _ = data + + async def restore(self) -> io.IOBase: + return io.BytesIO(b"snapshot") + + async def restorable(self) -> bool: + return True + + +class _ResumeCodexSession(_CodexSession): + def __init__(self, asset_name: str, *, resolved_binary_path: str, codex_path: Path) -> None: + super().__init__(asset_name, resolved_binary_path=resolved_binary_path) + self.state.snapshot = _RestorableSnapshot(id="resume") + self.codex_path = codex_path + self.hydrated = False + self.existing_paths: set[str] = set() + + async def _exec_internal( + self, + *command: str | Path, + timeout: float | None = None, + ) -> ExecResult: + _ = timeout + cmd = tuple(str(part) for part in command) + self.exec_calls.append(cmd) + if cmd[:2] == ("test", "-e"): + return ExecResult( + stdout=b"", + stderr=b"", + exit_code=0 if cmd[2] in self.existing_paths else 1, + ) + if cmd[:2] == ("sh", "-lc") and "find " in cmd[2] and "head -n 1" in cmd[2]: + return ExecResult( + stdout=f"{self.resolved_binary_path}\n".encode(), + stderr=b"", + exit_code=0, + ) + if cmd[:1] == ("cp",): + self.existing_paths.add(cmd[2]) + return ExecResult(stdout=b"", stderr=b"", exit_code=0) + + async def hydrate_workspace(self, data: io.IOBase) -> None: + _ = data + self.hydrated = True + + +def _tar_gz_bytes(*, members: dict[str, bytes]) -> bytes: + buf = io.BytesIO() + with tarfile.open(fileobj=buf, mode="w:gz") as archive: + for name, payload in members.items(): + info = tarfile.TarInfo(name=name) + info.size = len(payload) + archive.addfile(info, io.BytesIO(payload)) + return buf.getvalue() + + @pytest.mark.asyncio async def test_base_sandbox_session_uses_current_working_directory_for_local_file_sources( monkeypatch: pytest.MonkeyPatch, @@ -100,7 +190,9 @@ async def test_base_sandbox_session_uses_current_working_directory_for_local_fil source.write_text("hello", encoding="utf-8") monkeypatch.chdir(tmp_path) session = _RecordingSession( - Manifest(entries={"copied.txt": LocalFile(src=Path("source.txt"))}), + Manifest( + entries={"copied.txt": LocalFile(src=Path("source.txt"))}, + ), ) result = await session.apply_manifest() @@ -180,3 +272,118 @@ async def test_apply_manifest_raises_on_chgrp_failure() -> None: assert ("chgrp", "sandbox-user", "/workspace/copied.txt") in session.exec_calls assert not any(call[0] == "chmod" for call in session.exec_calls) + + +@pytest.mark.asyncio +async def test_codex_artifact_downloads_resolved_release_asset_inside_unix_box() -> None: + session = _CodexSession( + "codex-x86_64-unknown-linux-gnu.tar.gz", + resolved_binary_path="/workspace/.codex_bin/.codex-install-123/codex", + ) + entry = Codex(version="v1.2.3") + archive_bytes = _tar_gz_bytes(members={"codex": b"#!/bin/sh\n"}) + + class _FakeResponse: + headers = {"Content-Length": str(len(archive_bytes))} + + def raise_for_status(self) -> None: + return None + + def iter_bytes(self): + yield archive_bytes[:5] + yield archive_bytes[5:] + + class _FakeStreamContext: + def __enter__(self) -> _FakeResponse: + return _FakeResponse() + + def __exit__(self, exc_type, exc, tb) -> None: + return None + + def _fake_stream(url: str) -> _FakeStreamContext: + _ = url + return _FakeStreamContext() + + original_stream = codex_module._stream_release_asset + codex_module._stream_release_asset = _fake_stream + try: + result = await entry.apply(session, Path("/workspace/.codex_bin/codex"), Path("/ignored")) + finally: + codex_module._stream_release_asset = original_stream + + assert result == [] + assert any(path.name == "codex-x86_64-unknown-linux-gnu.tar.gz" for path in session.writes) + assert Path("/workspace/.codex_bin/codex") not in session.writes + assert any( + call[:2] == ("tar", "-xzf") + and call[2].endswith("/codex-x86_64-unknown-linux-gnu.tar.gz") + and call[3:5] == ("-C", call[4]) + and "/.codex-install-" in call[2] + for call in session.exec_calls + ) + assert ( + "cp", + "/workspace/.codex_bin/.codex-install-123/codex", + "/workspace/.codex_bin/codex", + ) in session.exec_calls + assert ("chmod", "0755", "/workspace/.codex_bin/codex") in session.exec_calls + + +@pytest.mark.asyncio +async def test_codex_artifact_rejects_windows_release_assets() -> None: + session = _CodexSession( + "codex-x86_64-pc-windows-msvc.exe.tar.gz", + resolved_binary_path="/workspace/.codex_bin/.codex-install-456/codex.exe", + ) + entry = Codex() + + with pytest.raises(RuntimeError, match="Windows Codex artifacts are not supported"): + await entry.apply(session, Path("/workspace/.codex_bin/codex.exe"), Path("/ignored")) + + +@pytest.mark.asyncio +async def test_base_session_reapplies_missing_codex_entry_after_snapshot_restore() -> None: + codex_path = Path("/workspace/.codex_bin/codex") + session = _ResumeCodexSession( + "codex-x86_64-unknown-linux-gnu.tar.gz", + resolved_binary_path="/workspace/.codex_bin/.codex-install-789/codex", + codex_path=codex_path, + ) + session.state.manifest = Manifest(entries={".codex_bin/codex": Codex(version="v1.2.3")}) + archive_bytes = _tar_gz_bytes(members={"codex": b"#!/bin/sh\n"}) + + class _FakeResponse: + headers = {"Content-Length": str(len(archive_bytes))} + + def raise_for_status(self) -> None: + return None + + def iter_bytes(self): + yield archive_bytes + + class _FakeStreamContext: + def __enter__(self) -> _FakeResponse: + return _FakeResponse() + + def __exit__(self, exc_type, exc, tb) -> None: + return None + + def _fake_stream(url: str) -> _FakeStreamContext: + _ = url + return _FakeStreamContext() + + original_stream = codex_module._stream_release_asset + codex_module._stream_release_asset = _fake_stream + try: + await session.start() + finally: + codex_module._stream_release_asset = original_stream + + assert session.hydrated is True + assert ("test", "-e", str(codex_path)) in session.exec_calls + assert ( + "cp", + "/workspace/.codex_bin/.codex-install-789/codex", + str(codex_path), + ) in session.exec_calls + assert str(codex_path) in session.existing_paths diff --git a/tests/test_sandbox_extract.py b/tests/test_sandbox_extract.py index 244c8fc851..5f7d105a88 100644 --- a/tests/test_sandbox_extract.py +++ b/tests/test_sandbox_extract.py @@ -166,6 +166,33 @@ def read(self, size: int = -1) -> bytes: return self._buffer.read(size) +class _ChunkedBinaryStream(io.IOBase): + def __init__(self, chunks: list[bytes]) -> None: + self._chunks = list(chunks) + self.headers = {"Content-Length": str(sum(len(chunk) for chunk in chunks))} + + def read(self, size: int = -1) -> bytes: + if not self._chunks: + return b"" + if size < 0: + data = b"".join(self._chunks) + self._chunks.clear() + return data + + remaining = size + out = bytearray() + while remaining > 0 and self._chunks: + chunk = self._chunks[0] + if len(chunk) <= remaining: + out.extend(self._chunks.pop(0)) + remaining -= len(chunk) + continue + out.extend(chunk[:remaining]) + self._chunks[0] = chunk[remaining:] + remaining = 0 + return bytes(out) + + def test_zipfile_compatible_stream_wraps_streams_without_seekable() -> None: raw_stream = _NoSeekableZipStream(_zip_bytes(members={"file.txt": b"hello"}).getvalue()) @@ -175,6 +202,22 @@ def test_zipfile_compatible_stream_wraps_streams_without_seekable() -> None: assert archive.read("file.txt") == b"hello" +@pytest.mark.asyncio +async def test_unix_local_write_accepts_chunked_non_seekable_binary_stream(tmp_path: Path) -> None: + session = _build_session(tmp_path) + await session.start() + try: + await session.write( + Path("streamed.bin"), + _ChunkedBinaryStream([b"hello ", b"from ", b"stream"]), + ) + finally: + await session.shutdown() + + workspace = Path(session.state.manifest.root) + assert (workspace / "streamed.bin").read_bytes() == b"hello from stream" + + @pytest.mark.asyncio async def test_extract_tar_rejects_symlinked_parent_paths(tmp_path: Path) -> None: session = _build_session(tmp_path) diff --git a/tests/test_sandbox_manifest.py b/tests/test_sandbox_manifest.py index 8f840c07f8..5f493308b5 100644 --- a/tests/test_sandbox_manifest.py +++ b/tests/test_sandbox_manifest.py @@ -2,7 +2,13 @@ import pytest -from agents.sandbox.entries import Dir, File, GCSMount +from agents.sandbox.codex_config import ( + DEFAULT_CODEX_VERSION, + CodexConfig, + apply_codex_to_manifest, + manifest_has_codex_entry, +) +from agents.sandbox.entries import Codex, Dir, File, GCSMount from agents.sandbox.errors import InvalidManifestPathError from agents.sandbox.manifest import Manifest @@ -131,9 +137,86 @@ def test_manifest_describe_preserves_tree_rendering_after_renderer_extract() -> }, ) - assert manifest.describe(depth=2) == ( - "/workspace\n" - "├── data/ # /workspace/data — shared data\n" - "└── repo/ # /workspace/repo — project root\n" - " └── README.md # /workspace/repo/README.md — overview\n" + description = manifest.describe(depth=2) + + assert description.startswith("/workspace\n") + assert "data/" in description + assert "/workspace/data" in description + assert "repo/" in description + assert "/workspace/repo/README.md" in description + + +def test_apply_codex_to_manifest_adds_codex_entry_at_configured_path() -> None: + manifest = apply_codex_to_manifest( + Manifest(), + CodexConfig(path="tools/codex"), + ) + + validated = manifest.validated_entries() + + assert Path("tools/codex") in validated + entry = validated[Path("tools/codex")] + assert isinstance(entry, Codex) + assert entry.version == DEFAULT_CODEX_VERSION + + +def test_apply_codex_to_manifest_treats_home_relative_path_as_workspace_relative() -> None: + manifest = apply_codex_to_manifest( + Manifest(), + CodexConfig(path="~/.codex/codex"), + ) + + validated = manifest.validated_entries() + + assert Path(".codex/codex") in validated + entry = validated[Path(".codex/codex")] + assert isinstance(entry, Codex) + + +def test_apply_codex_to_manifest_preserves_explicit_entry_at_configured_path() -> None: + explicit = File(content=b"custom") + manifest = apply_codex_to_manifest( + Manifest( + entries={"tools/codex": explicit}, + ), + CodexConfig(path="tools/codex"), + ) + + validated = manifest.validated_entries() + + preserved = validated["tools/codex"] + assert isinstance(preserved, File) + assert preserved == explicit + + +def test_apply_codex_to_manifest_accepts_absolute_path_within_manifest_root() -> None: + manifest = apply_codex_to_manifest( + Manifest(root="/workspace"), + CodexConfig(path="/workspace/tools/codex"), + ) + + validated = manifest.validated_entries() + + assert Path("tools/codex") in validated + entry = validated[Path("tools/codex")] + assert isinstance(entry, Codex) + + +def test_apply_codex_to_manifest_rejects_absolute_path_outside_manifest_root() -> None: + with pytest.raises(InvalidManifestPathError, match="must be relative"): + apply_codex_to_manifest( + Manifest(root="/workspace"), + CodexConfig(path="/tmp/codex"), + ) + + +def test_manifest_has_codex_entry_accepts_absolute_default_root_path_after_root_rewrite() -> None: + manifest = Manifest( + root="/tmp/session-root", + entries={"tools/codex": Codex(version=DEFAULT_CODEX_VERSION)}, + ) + + assert manifest_has_codex_entry( + manifest, + CodexConfig(path="/workspace/tools/codex"), ) diff --git a/tests/test_sandbox_runtime.py b/tests/test_sandbox_runtime.py index 9909bb2964..a3105afa7a 100644 --- a/tests/test_sandbox_runtime.py +++ b/tests/test_sandbox_runtime.py @@ -26,6 +26,11 @@ from agents.run_state import RunState, _build_agent_identity_map from agents.sandbox import Manifest, SandboxAgent, SandboxRunConfig from agents.sandbox.capabilities import Capability +from agents.sandbox.codex_config import ( + CodexConfig, + apply_codex_to_manifest, + apply_codex_to_session_state, +) from agents.sandbox.entries import File from agents.sandbox.errors import ExecNonZeroError, ExecTransportError, InvalidManifestPathError from agents.sandbox.runtime import SandboxRuntime @@ -259,6 +264,7 @@ def _get_reasoning_item() -> ResponseReasoningItem: class _CreateKwargs(TypedDict): snapshot: object | None manifest: Manifest | None + codex: bool | CodexConfig options: dict[str, str] @@ -277,24 +283,32 @@ async def create( *, snapshot: object | None = None, manifest: Manifest | None = None, + codex: bool | CodexConfig = False, options: dict[str, str], ) -> SandboxSession: + base_manifest = manifest if manifest is not None else self.inner_session.state.manifest self.create_kwargs = { "snapshot": snapshot, - "manifest": manifest, + "manifest": apply_codex_to_manifest(base_manifest, codex), + "codex": codex, "options": options, } - if manifest is not None: - self.inner_session.state.manifest = manifest + if self.create_kwargs["manifest"] is not None: + self.inner_session.state.manifest = self.create_kwargs["manifest"] return self.session async def delete(self, session: SandboxSession) -> SandboxSession: self.delete_calls += 1 return session - async def resume(self, state: SandboxSessionState) -> SandboxSession: - self.resume_state = state - self.inner_session.state = state + async def resume( + self, + state: SandboxSessionState, + *, + codex: bool | CodexConfig = False, + ) -> SandboxSession: + self.resume_state = apply_codex_to_session_state(state, codex) + self.inner_session.state = self.resume_state return self.session def deserialize_session_state(self, payload: dict[str, object]) -> SandboxSessionState: @@ -313,9 +327,11 @@ async def create( *, snapshot: object | None = None, manifest: Manifest | None = None, + codex: bool | CodexConfig = False, options: None = None, ) -> SandboxSession: _ = (snapshot, options) + manifest = apply_codex_to_manifest(manifest, codex) self.created_manifests.append(manifest) assert manifest is not None session = _FakeSession(manifest) @@ -324,8 +340,14 @@ async def create( async def delete(self, session: SandboxSession) -> SandboxSession: return session - async def resume(self, state: SandboxSessionState) -> SandboxSession: - return self._wrap_session(_FakeSession(state.manifest)) + async def resume( + self, + state: SandboxSessionState, + *, + codex: bool | CodexConfig = False, + ) -> SandboxSession: + resumed_state = apply_codex_to_session_state(state, codex) + return self._wrap_session(_FakeSession(resumed_state.manifest)) def deserialize_session_state(self, payload: dict[str, object]) -> SandboxSessionState: return SandboxSessionState.model_validate(payload) @@ -567,6 +589,26 @@ def _sandbox_run_config(client: _FakeClient | None = None) -> RunConfig: ) +def _unix_local_manifest(**kwargs: Any) -> Manifest: + return Manifest(**kwargs) + + +def _unix_local_run_config( + *, + client: UnixLocalSandboxClient | None = None, + session_state: SandboxSessionState | None = None, + manifest: Manifest | None = None, +) -> RunConfig: + sandbox_kwargs: dict[str, Any] = { + "client": client or UnixLocalSandboxClient(), + } + if session_state is not None: + sandbox_kwargs["session_state"] = session_state + else: + sandbox_kwargs["manifest"] = manifest or _unix_local_manifest() + return RunConfig(sandbox=SandboxRunConfig(**sandbox_kwargs)) + + @pytest.mark.asyncio async def test_runner_merges_sandbox_instructions_and_tools() -> None: model = FakeModel(initial_output=[get_final_output_message("done")]) @@ -691,6 +733,7 @@ async def test_runner_does_not_close_injected_sandbox_session() -> None: model=model, instructions="Base instructions.", default_manifest=default_manifest, + codex=False, ) result = await Runner.run( @@ -725,6 +768,7 @@ async def test_runner_does_not_restart_running_injected_sandbox_session() -> Non name="sandbox", model=model, instructions="Base instructions.", + codex=False, ) result = await Runner.run( @@ -739,6 +783,44 @@ async def test_runner_does_not_restart_running_injected_sandbox_session() -> Non assert injected_session.shutdown_calls == 0 +@pytest.mark.asyncio +async def test_runner_passes_codex_requirement_to_client_created_sessions() -> None: + session = _FakeSession(Manifest()) + client = _FakeClient(session) + agent = SandboxAgent( + name="sandbox", + model=FakeModel(initial_output=[get_final_output_message("done")]), + instructions="Base instructions.", + ) + + result = await Runner.run(agent, "hello", run_config=_sandbox_run_config(client)) + + assert result.final_output == "done" + assert client.create_kwargs is not None + assert client.create_kwargs["codex"] is True + manifest = client.create_kwargs["manifest"] + assert manifest is not None + manifest_paths = {manifest._coerce_rel_path(path) for path in manifest.entries} + assert Path(".codex/codex") in manifest_paths + + +@pytest.mark.asyncio +async def test_runner_rejects_injected_session_missing_required_codex() -> None: + injected_session = _FakeSession(Manifest(entries={"session.txt": File(content=b"session")})) + agent = SandboxAgent( + name="sandbox", + model=FakeModel(initial_output=[get_final_output_message("done")]), + instructions="Base instructions.", + ) + + with pytest.raises(UserError, match="missing Codex"): + await Runner.run( + agent, + "hello", + run_config=RunConfig(sandbox=SandboxRunConfig(session=injected_session)), + ) + + @pytest.mark.asyncio async def test_runner_uses_public_sandbox_agent_for_dynamic_instructions() -> None: model = FakeModel(initial_output=[get_final_output_message("done")]) @@ -1013,7 +1095,7 @@ def approval_tool() -> str: @pytest.mark.asyncio async def test_unix_local_client_rewrites_default_manifest_root_to_temp_workspace() -> None: client = UnixLocalSandboxClient() - manifest = Manifest(entries={"default.txt": File(content=b"default")}) + manifest = _unix_local_manifest(entries={"default.txt": File(content=b"default")}) session = await client.create(manifest=manifest, options=None) workspace_root = Path(session.state.manifest.root) @@ -1039,12 +1121,13 @@ async def test_runner_allows_fresh_unix_local_sessions_without_options() -> None name="sandbox", model=FakeModel(initial_output=[get_final_output_message("done")]), instructions="Base instructions.", + codex=False, ) result = await Runner.run( agent, "hello", - run_config=RunConfig(sandbox=SandboxRunConfig(client=UnixLocalSandboxClient())), + run_config=_unix_local_run_config(), ) assert result.final_output == "done" @@ -1054,7 +1137,7 @@ async def test_runner_allows_fresh_unix_local_sessions_without_options() -> None async def test_unix_local_client_delete_preserves_caller_owned_workspace_root() -> None: client = UnixLocalSandboxClient() workspace_root = Path(tempfile.mkdtemp(prefix="caller-owned-")) - manifest = Manifest(root=str(workspace_root)) + manifest = _unix_local_manifest(root=str(workspace_root)) session = await client.create(manifest=manifest, options=None) assert cast(UnixLocalSandboxSessionState, session.state).workspace_root_owned is False @@ -1070,25 +1153,21 @@ async def test_unix_local_runner_cleanup_preserves_resumed_caller_owned_workspac workspace_root = Path(tempfile.mkdtemp(prefix="resumed-owned-")) state = UnixLocalSandboxSessionState( session_id=uuid.uuid4(), - manifest=Manifest(root=str(workspace_root)), + manifest=_unix_local_manifest(root=str(workspace_root)), snapshot=NoopSnapshot(id=str(uuid.uuid4())), ) agent = SandboxAgent( name="sandbox", model=FakeModel(initial_output=[get_final_output_message("done")]), instructions="Base instructions.", + codex=False, ) try: result = await Runner.run( agent, "hello", - run_config=RunConfig( - sandbox=SandboxRunConfig( - client=UnixLocalSandboxClient(), - session_state=state, - ) - ), + run_config=_unix_local_run_config(session_state=state), ) finally: assert workspace_root.exists() @@ -1101,7 +1180,10 @@ async def test_unix_local_runner_cleanup_preserves_resumed_caller_owned_workspac async def test_unix_local_read_and_write_reject_paths_outside_workspace_root() -> None: client = UnixLocalSandboxClient() workspace_root = Path(tempfile.mkdtemp(prefix="workspace-root-")) - session = await client.create(manifest=Manifest(root=str(workspace_root)), options=None) + session = await client.create( + manifest=_unix_local_manifest(root=str(workspace_root)), + options=None, + ) try: with pytest.raises(InvalidManifestPathError, match="must not escape root"): @@ -1113,6 +1195,39 @@ async def test_unix_local_read_and_write_reject_paths_outside_workspace_root() - shutil.rmtree(workspace_root) +@pytest.mark.asyncio +async def test_unix_local_rm_recursive_ignores_missing_paths() -> None: + client = UnixLocalSandboxClient() + workspace_root = Path(tempfile.mkdtemp(prefix="workspace-root-")) + session = await client.create( + manifest=_unix_local_manifest(root=str(workspace_root)), + options=None, + ) + + try: + await session.rm("missing-dir", recursive=True) + finally: + await client.delete(session) + shutil.rmtree(workspace_root) + + +@pytest.mark.asyncio +async def test_unix_local_rm_non_recursive_still_errors_for_missing_paths() -> None: + client = UnixLocalSandboxClient() + workspace_root = Path(tempfile.mkdtemp(prefix="workspace-root-")) + session = await client.create( + manifest=_unix_local_manifest(root=str(workspace_root)), + options=None, + ) + + try: + with pytest.raises(ExecNonZeroError): + await session.rm("missing-dir") + finally: + await client.delete(session) + shutil.rmtree(workspace_root) + + @pytest.mark.asyncio async def test_runner_streamed_ignores_sandbox_cleanup_failures_after_success() -> None: session = _FailingStopSession(Manifest()) @@ -1292,7 +1407,7 @@ async def test_runner_streamed_run_loop_task_waits_for_sandbox_cleanup_and_persi async def test_runner_rejects_unix_local_manifest_user_and_group_provisioning() -> None: workspace_root = Path(tempfile.mkdtemp(prefix="unix-local-users-")) session = await UnixLocalSandboxClient().create( - manifest=Manifest( + manifest=_unix_local_manifest( root=str(workspace_root), users=[User(name="sandbox-user")], ), @@ -1341,12 +1456,13 @@ def approval_tool() -> str: tools=[approval_tool], capabilities=[file_capability], model_settings=ModelSettings(tool_choice="required"), + codex=False, ) first_run = await Runner.run( agent, "hello", - run_config=RunConfig(sandbox=SandboxRunConfig(client=client)), + run_config=_unix_local_run_config(client=client), ) assert len(first_run.interruptions) == 1 @@ -1386,6 +1502,7 @@ def approval_tool() -> str: tools=[approval_tool], capabilities=[_SessionFileCapability()], model_settings=ModelSettings(tool_choice="required"), + codex=False, ) restored_state = await RunState.from_json(resumed_agent, state_json) @@ -1393,7 +1510,7 @@ def approval_tool() -> str: resumed = await Runner.run( resumed_agent, restored_state, - run_config=RunConfig(sandbox=SandboxRunConfig(client=client)), + run_config=_unix_local_run_config(client=client), ) assert resumed.final_output == "done" @@ -1422,6 +1539,7 @@ def approval_tool() -> str: model=worker_model, instructions="Worker instructions.", tools=[approval_tool], + codex=False, ) triage = SandboxAgent( name="triage", @@ -1429,6 +1547,7 @@ def approval_tool() -> str: instructions="Triage instructions.", capabilities=[file_capability], handoffs=[worker], + codex=False, ) worker.handoffs = [triage] triage_model.add_multiple_turn_outputs( @@ -1452,7 +1571,7 @@ def approval_tool() -> str: first_run = await Runner.run( triage, "hello", - run_config=RunConfig(sandbox=SandboxRunConfig(client=client)), + run_config=_unix_local_run_config(client=client), ) assert len(first_run.interruptions) == 1 @@ -1472,6 +1591,7 @@ def approval_tool() -> str: model=resumed_worker_model, instructions="Worker instructions.", tools=[approval_tool], + codex=False, ) resumed_triage = SandboxAgent( name="triage", @@ -1479,6 +1599,7 @@ def approval_tool() -> str: instructions="Triage instructions.", capabilities=[_SessionFileCapability()], handoffs=[resumed_worker], + codex=False, ) resumed_worker.handoffs = [resumed_triage] resumed_worker_model.add_multiple_turn_outputs([[get_handoff_tool_call(resumed_triage)]]) @@ -1500,7 +1621,7 @@ def approval_tool() -> str: resumed = await Runner.run( resumed_triage, restored_state, - run_config=RunConfig(sandbox=SandboxRunConfig(client=client)), + run_config=_unix_local_run_config(client=client), ) assert resumed.final_output == "done" @@ -1528,12 +1649,14 @@ def approval_tool() -> str: model=first_model, instructions="First instructions.", capabilities=[file_capability], + codex=False, ) second = SandboxAgent( name="sandbox", model=second_model, instructions="Second instructions.", tools=[approval_tool], + codex=False, ) first.handoffs = [second] second.handoffs = [first] @@ -1567,7 +1690,7 @@ def approval_tool() -> str: first_run = await Runner.run( first, "hello", - run_config=RunConfig(sandbox=SandboxRunConfig(client=client)), + run_config=_unix_local_run_config(client=client), ) state = first_run.to_state() @@ -1580,7 +1703,7 @@ def approval_tool() -> str: resumed = await Runner.run( first, state, - run_config=RunConfig(sandbox=SandboxRunConfig(client=client)), + run_config=_unix_local_run_config(client=client), ) assert resumed.final_output == "done" @@ -1704,8 +1827,8 @@ def test_session_manager_generates_collision_free_resume_keys_for_literal_suffix async def test_session_manager_preserves_untouched_run_state_sessions_on_cleanup() -> None: manifest = Manifest(entries={"README.md": File(content=b"duplicate resume")}) client = _FakeClient(_FakeSession(manifest)) - triage = SandboxAgent(name="triage", model=FakeModel(), instructions="Triage.") - worker = SandboxAgent(name="worker", model=FakeModel(), instructions="Worker.") + triage = SandboxAgent(name="triage", model=FakeModel(), instructions="Triage.", codex=False) + worker = SandboxAgent(name="worker", model=FakeModel(), instructions="Worker.", codex=False) triage.handoffs = [worker] worker.handoffs = [triage] triage_session_state = client.serialize_session_state( @@ -2003,12 +2126,14 @@ def approval_tool() -> str: model=first_model, instructions="First instructions.", capabilities=[file_capability], + codex=False, ) second = SandboxAgent( name="sandbox", model=second_model, instructions="Second instructions.", tools=[approval_tool], + codex=False, ) first.handoffs = [second] second.handoffs = [first] @@ -2031,7 +2156,7 @@ def approval_tool() -> str: first_run = await Runner.run( first, "hello", - run_config=RunConfig(sandbox=SandboxRunConfig(client=client)), + run_config=_unix_local_run_config(client=client), ) state = first_run.to_state() @@ -2044,12 +2169,14 @@ def approval_tool() -> str: model=resumed_first_model, instructions="First instructions.", capabilities=[_SessionFileCapability()], + codex=False, ) resumed_second = SandboxAgent( name="sandbox", model=resumed_second_model, instructions="Second instructions.", tools=[approval_tool], + codex=False, ) resumed_first.handoffs = [resumed_second] resumed_second.handoffs = [resumed_first] @@ -2072,7 +2199,7 @@ def approval_tool() -> str: resumed = await Runner.run( resumed_first, restored_state, - run_config=RunConfig(sandbox=SandboxRunConfig(client=client)), + run_config=_unix_local_run_config(client=client), ) assert resumed.final_output == "done" @@ -2109,12 +2236,13 @@ def approval_tool() -> str: instructions="Base instructions.", tools=[approval_tool], capabilities=[_SessionFileCapability()], + codex=False, ) first_run = await Runner.run( agent, "hello", - run_config=RunConfig(sandbox=SandboxRunConfig(client=client)), + run_config=_unix_local_run_config(client=client), ) state = first_run.to_state() assert state._sandbox is not None @@ -2143,6 +2271,7 @@ def approval_tool() -> str: instructions="Base instructions.", tools=[approval_tool], capabilities=[_SessionFileCapability()], + codex=False, ) restored_state = await RunState.from_json(resumed_agent, state.to_json()) @@ -2150,7 +2279,7 @@ def approval_tool() -> str: resumed = await Runner.run( resumed_agent, restored_state, - run_config=RunConfig(sandbox=SandboxRunConfig(client=client)), + run_config=_unix_local_run_config(client=client), ) assert resumed.final_output == "done" @@ -2170,7 +2299,7 @@ def approval_tool() -> str: async def test_unix_local_exec_confines_commands_to_workspace_root() -> None: workspace_root = Path(tempfile.mkdtemp(prefix="unix-local-exec-")) session = await UnixLocalSandboxClient().create( - manifest=Manifest(root=str(workspace_root)), + manifest=_unix_local_manifest(root=str(workspace_root)), options=None, ) @@ -2201,7 +2330,7 @@ async def test_unix_local_exec_rejects_when_confinement_is_unavailable( ) -> None: workspace_root = Path(tempfile.mkdtemp(prefix="unix-local-exec-")) session = await UnixLocalSandboxClient().create( - manifest=Manifest(root=str(workspace_root)), + manifest=_unix_local_manifest(root=str(workspace_root)), options=None, ) unix_local = cast(Any, unix_local_module) @@ -2223,7 +2352,7 @@ async def test_unix_local_exec_runs_without_wrapper_on_linux( ) -> None: workspace_root = Path(tempfile.mkdtemp(prefix="unix-local-exec-")) session = await UnixLocalSandboxClient().create( - manifest=Manifest(root=str(workspace_root)), + manifest=_unix_local_manifest(root=str(workspace_root)), options=None, ) unix_local = cast(Any, unix_local_module) @@ -2246,7 +2375,7 @@ def test_unix_local_confined_exec_command_allows_common_darwin_interpreter_roots session = UnixLocalSandboxSession.from_state( UnixLocalSandboxSessionState( session_id=uuid.uuid4(), - manifest=Manifest(root=str(workspace_root)), + manifest=_unix_local_manifest(root=str(workspace_root)), snapshot=NoopSnapshot(id="darwin"), workspace_root_owned=False, ) diff --git a/tests/test_sandbox_session_utils.py b/tests/test_sandbox_session_utils.py index 4cf0e8f444..dc67c81b86 100644 --- a/tests/test_sandbox_session_utils.py +++ b/tests/test_sandbox_session_utils.py @@ -7,6 +7,8 @@ import pytest +from agents.sandbox.entries.codex import resolve_codex_target_triple_for_target +from agents.sandbox.errors import UnsupportedCodexTargetError from agents.sandbox.files import EntryKind, FileEntry from agents.sandbox.manifest import Manifest from agents.sandbox.session import UCStartEvent @@ -60,6 +62,51 @@ async def shutdown(self) -> None: return +class _ScriptedExecSession(BaseSandboxSession): + def __init__(self, responses: dict[tuple[str, ...], list[ExecResult] | ExecResult]) -> None: + self.state = SandboxSessionState( + manifest=Manifest(), + snapshot=NoopSnapshot(id="noop"), + ) + self.responses: dict[tuple[str, ...], list[ExecResult]] = {} + for command, response in responses.items(): + if isinstance(response, ExecResult): + self.responses[command] = [response] + else: + self.responses[command] = list(response) + + async def _exec_internal( + self, + *command: str | Path, + timeout: float | None = None, + ) -> ExecResult: + _ = timeout + key = tuple(str(part) for part in command) + if key not in self.responses or not self.responses[key]: + return ExecResult(stdout=b"", stderr=b"", exit_code=1) + return self.responses[key].pop(0) + + async def read(self, path: Path) -> io.IOBase: + _ = path + raise AssertionError("read() should not be called in this test") + + async def write(self, path: Path, data: io.IOBase) -> None: + _ = (path, data) + raise AssertionError("write() should not be called in this test") + + async def running(self) -> bool: + return True + + async def persist_workspace(self) -> io.IOBase: + return io.BytesIO() + + async def hydrate_workspace(self, data: io.IOBase) -> None: + _ = data + + async def shutdown(self) -> None: + return + + def test_safe_decode_truncates_and_appends_ellipsis() -> None: assert _safe_decode(b"abcdef", max_chars=3) == "abc…" @@ -163,3 +210,197 @@ async def test_exec_shell_true_preserves_single_shell_snippet() -> None: await session.exec("echo hello && echo goodbye", shell=True) assert session.last_command == ("sh", "-lc", "echo hello && echo goodbye") + + +@pytest.mark.asyncio +async def test_resolve_codex_github_asset_name_linux_gnu() -> None: + session = _ScriptedExecSession( + { + ("uname", "-s"): ExecResult(stdout=b"Linux\n", stderr=b"", exit_code=0), + ("uname", "-m"): ExecResult(stdout=b"x86_64\n", stderr=b"", exit_code=0), + ("getconf", "GNU_LIBC_VERSION"): ExecResult( + stdout=b"glibc 2.39\n", + stderr=b"", + exit_code=0, + ), + } + ) + + assert ( + await session.resolve_codex_github_asset_name() == "codex-x86_64-unknown-linux-gnu.tar.gz" + ) + + +@pytest.mark.asyncio +async def test_resolve_codex_github_asset_name_linux_musl() -> None: + session = _ScriptedExecSession( + { + ("uname", "-s"): ExecResult(stdout=b"Linux\n", stderr=b"", exit_code=0), + ("uname", "-m"): ExecResult(stdout=b"amd64\n", stderr=b"", exit_code=0), + ("getconf", "GNU_LIBC_VERSION"): ExecResult(stdout=b"", stderr=b"", exit_code=1), + ("ldd", "--version"): ExecResult( + stdout=b"", + stderr=b"musl libc (x86_64)\n", + exit_code=1, + ), + } + ) + + assert ( + await session.resolve_codex_github_asset_name() == "codex-x86_64-unknown-linux-musl.tar.gz" + ) + + +@pytest.mark.asyncio +async def test_resolve_codex_github_asset_name_linux_aarch64_gnu() -> None: + session = _ScriptedExecSession( + { + ("uname", "-s"): ExecResult(stdout=b"Linux\n", stderr=b"", exit_code=0), + ("uname", "-m"): ExecResult(stdout=b"aarch64\n", stderr=b"", exit_code=0), + ("getconf", "GNU_LIBC_VERSION"): ExecResult( + stdout=b"glibc 2.39\n", + stderr=b"", + exit_code=0, + ), + } + ) + + assert ( + await session.resolve_codex_github_asset_name() == "codex-aarch64-unknown-linux-gnu.tar.gz" + ) + + +@pytest.mark.asyncio +async def test_resolve_codex_github_asset_name_darwin() -> None: + session = _ScriptedExecSession( + { + ("uname", "-s"): ExecResult(stdout=b"Darwin\n", stderr=b"", exit_code=0), + ("uname", "-m"): ExecResult(stdout=b"x86_64\n", stderr=b"", exit_code=0), + } + ) + + assert await session.resolve_codex_github_asset_name() == "codex-x86_64-apple-darwin.tar.gz" + + +@pytest.mark.asyncio +async def test_resolve_codex_github_asset_name_darwin_arm64() -> None: + session = _ScriptedExecSession( + { + ("uname", "-s"): ExecResult(stdout=b"Darwin\n", stderr=b"", exit_code=0), + ("uname", "-m"): ExecResult(stdout=b"arm64\n", stderr=b"", exit_code=0), + } + ) + + assert await session.resolve_codex_github_asset_name() == "codex-aarch64-apple-darwin.tar.gz" + + +@pytest.mark.asyncio +async def test_resolve_codex_github_asset_name_windows() -> None: + session = _ScriptedExecSession( + { + ("uname", "-s"): ExecResult(stdout=b"", stderr=b"", exit_code=1), + ("cmd", "/c", "echo", "%OS%"): ExecResult( + stdout=b"Windows_NT\r\n", + stderr=b"", + exit_code=0, + ), + ("cmd", "/c", "echo", "%PROCESSOR_ARCHITECTURE%"): ExecResult( + stdout=b"AMD64\r\n", + stderr=b"", + exit_code=0, + ), + } + ) + + assert ( + await session.resolve_codex_github_asset_name() == "codex-x86_64-pc-windows-msvc.exe.tar.gz" + ) + + +@pytest.mark.asyncio +async def test_resolve_codex_github_asset_name_windows_arm64() -> None: + session = _ScriptedExecSession( + { + ("uname", "-s"): ExecResult(stdout=b"", stderr=b"", exit_code=1), + ("cmd", "/c", "echo", "%OS%"): ExecResult( + stdout=b"Windows_NT\r\n", + stderr=b"", + exit_code=0, + ), + ("cmd", "/c", "echo", "%PROCESSOR_ARCHITECTURE%"): ExecResult( + stdout=b"ARM64\r\n", + stderr=b"", + exit_code=0, + ), + } + ) + + assert ( + await session.resolve_codex_github_asset_name() + == "codex-aarch64-pc-windows-msvc.exe.tar.gz" + ) + + +def test_resolve_codex_target_triple_reports_unsupported_os() -> None: + with pytest.raises( + UnsupportedCodexTargetError, + match=( + "Unsupported Codex target operating system: freebsd. " + "Available operating systems: linux, darwin, windows." + ), + ) as exc_info: + resolve_codex_target_triple_for_target( + target_os="freebsd", + target_arch="x86_64", + ) + + assert exc_info.value.reason == "operating_system" + assert exc_info.value.target_os == "freebsd" + assert exc_info.value.supported_operating_systems == ("linux", "darwin", "windows") + + +def test_resolve_codex_target_triple_reports_unsupported_architecture() -> None: + with pytest.raises( + UnsupportedCodexTargetError, + match=( + "Unsupported Codex target architecture for darwin: ppc64le. " + "Available architectures: x86_64, aarch64." + ), + ) as exc_info: + resolve_codex_target_triple_for_target( + target_os="darwin", + target_arch="ppc64le", + ) + + assert exc_info.value.reason == "architecture" + assert exc_info.value.target_arch == "ppc64le" + assert exc_info.value.supported_architectures == ("x86_64", "aarch64") + + +def test_resolve_codex_target_triple_normalizes_arm_aliases() -> None: + assert ( + resolve_codex_target_triple_for_target( + target_os="darwin", + target_arch="arm64", + ) + == "aarch64-apple-darwin" + ) + + +def test_resolve_codex_target_triple_reports_unsupported_linux_libc() -> None: + with pytest.raises( + UnsupportedCodexTargetError, + match=( + "Unsupported Linux libc variant for Codex target resolution: uclibc. " + "Available libc variants: gnu, musl." + ), + ) as exc_info: + resolve_codex_target_triple_for_target( + target_os="linux", + target_arch="x86_64", + linux_libc="uclibc", + ) + + assert exc_info.value.reason == "linux_libc" + assert exc_info.value.linux_libc == "uclibc" + assert exc_info.value.supported_linux_libc_variants == ("gnu", "musl") From 9e2e44cdb0a14f6d575c855e47096e62bd47ac8c Mon Sep 17 00:00:00 2001 From: Kazuhiro Sera Date: Mon, 16 Mar 2026 11:16:13 -0700 Subject: [PATCH 05/11] fix: block sandbox preparation on sequential input guardrails (#19) This pull request fixes a sandbox ordering bug that broke the contract of `InputGuardrail(run_in_parallel=False)` on the first turn. Before this change, both `Runner.run()` and `Runner.run_streamed()` called `SandboxRuntime.prepare_agent()` before running first-turn sequential input guardrails. In sandbox-backed runs, that meant a guardrail trip could still happen after sandbox side effects had already occurred. Concretely, a blocked run could still create and start a runner-owned sandbox session, start a stopped injected session, or apply capability-driven manifest deltas to an already-running injected session. That behavior is wrong because sequential input guardrails are supposed to run before the agent starts. For sandbox agents, "agent start" was effectively happening too early through sandbox preparation and session materialization. This change moves first-turn sequential input guardrails ahead of sandbox preparation for sandbox-enabled runs in both the non-streaming and streaming execution paths. Parallel input guardrails still run alongside the model work as before, so the fix is narrowly scoped to the blocking-before-start path. The streamed guardrail helper was also updated so early guardrail execution still records results correctly even before an agent span exists. The new regression tests cover all affected combinations: - non-streamed runner-owned sandbox sessions, - non-streamed running injected sessions, - streamed runner-owned sandbox sessions, - streamed running injected sessions. Each test verifies that when the guardrail trips, sandbox preparation produces no side effects: no session creation, no session start, and no live-session manifest materialization. --- src/agents/run.py | 43 +++++-- src/agents/run_internal/guardrails.py | 22 ++-- src/agents/run_internal/run_loop.py | 45 ++++++- tests/test_sandbox_runtime.py | 162 +++++++++++++++++++++++++- 4 files changed, 244 insertions(+), 28 deletions(-) diff --git a/src/agents/run.py b/src/agents/run.py index de808cb5dd..3015d3b781 100644 --- a/src/agents/run.py +++ b/src/agents/run.py @@ -678,6 +678,40 @@ def _finalize_result(result: RunResult) -> RunResult: try: while True: resuming_turn = is_resumed_state + all_input_guardrails = ( + starting_agent.input_guardrails + (run_config.input_guardrails or []) + if current_turn == 0 and not resuming_turn + else [] + ) + sequential_guardrails = [ + g for g in all_input_guardrails if not g.run_in_parallel + ] + parallel_guardrails = [g for g in all_input_guardrails if g.run_in_parallel] + sequential_results: list[InputGuardrailResult] = [] + if sandbox_runtime.enabled and sequential_guardrails: + # Blocking first-turn guardrails must run before sandbox prep so a tripwire + # can prevent session creation, startup, or live-session mutation. + try: + sequential_results = await run_input_guardrails( + starting_agent, + sequential_guardrails, + copy_input_items(original_input), + context_wrapper, + ) + except InputGuardrailTripwireTriggered: + session_input_items_for_persistence = ( + await persist_session_items_for_guardrail_trip( + session, + server_conversation_tracker, + session_input_items_for_persistence, + original_user_input, + run_state, + store=store_setting, + ) + ) + raise + sequential_guardrails = [] + current_bindings = bind_public_agent(current_agent) execution_agent = current_bindings.execution_agent prepared_sandbox = await sandbox_runtime.prepare_agent( @@ -1039,16 +1073,7 @@ def _finalize_result(result: RunResult) -> RunResult: ) if current_turn <= 1: - all_input_guardrails = starting_agent.input_guardrails + ( - run_config.input_guardrails or [] - ) - sequential_guardrails = [ - g for g in all_input_guardrails if not g.run_in_parallel - ] - parallel_guardrails = [g for g in all_input_guardrails if g.run_in_parallel] - try: - sequential_results = [] if sequential_guardrails: sequential_results = await run_input_guardrails( starting_agent, diff --git a/src/agents/run_internal/guardrails.py b/src/agents/run_internal/guardrails.py index 375cc37c25..1b04779d81 100644 --- a/src/agents/run_internal/guardrails.py +++ b/src/agents/run_internal/guardrails.py @@ -57,7 +57,7 @@ async def run_input_guardrails_with_queue( input: str | list[TResponseInputItem], context: RunContextWrapper[TContext], streamed_result: RunResultStreaming, - parent_span: Span[Any], + parent_span: Span[Any] | None, ) -> None: """Run guardrails concurrently and stream results into the queue.""" queue = streamed_result._input_guardrail_queue @@ -74,16 +74,18 @@ async def run_input_guardrails_with_queue( for t in guardrail_tasks: t.cancel() await asyncio.gather(*guardrail_tasks, return_exceptions=True) - _error_tracing.attach_error_to_span( - parent_span, - SpanError( - message="Guardrail tripwire triggered", - data={ - "guardrail": result.guardrail.get_name(), - "type": "input_guardrail", - }, - ), + span_error = SpanError( + message="Guardrail tripwire triggered", + data={ + "guardrail": result.guardrail.get_name(), + "type": "input_guardrail", + }, ) + if parent_span is not None: + _error_tracing.attach_error_to_span(parent_span, span_error) + else: + # Early first-turn streamed guardrails can run before the agent span exists. + _error_tracing.attach_error_to_current_span(span_error) queue.put_nowait(result) guardrail_results.append(result) break diff --git a/src/agents/run_internal/run_loop.py b/src/agents/run_internal/run_loop.py index 4d82835cb5..55ddf7c117 100644 --- a/src/agents/run_internal/run_loop.py +++ b/src/agents/run_internal/run_loop.py @@ -601,9 +601,48 @@ async def _save_stream_items_without_count( try: while True: + all_input_guardrails = ( + starting_agent.input_guardrails + (run_config.input_guardrails or []) + if current_turn == 0 and not is_resumed_state + else [] + ) + sequential_guardrails = [g for g in all_input_guardrails if not g.run_in_parallel] + parallel_guardrails = [g for g in all_input_guardrails if g.run_in_parallel] current_bindings = bind_public_agent(current_agent) execution_agent = current_bindings.execution_agent prepared_turn_input = copy_input_items(streamed_result.input) + if sandbox_runtime is not None and sandbox_runtime.enabled and sequential_guardrails: + # Mirror the non-streaming path: a blocking first-turn guardrail should fire + # before sandbox prep can create, start, or mutate sandbox state. + existing_input_guardrail_count = len(streamed_result.input_guardrail_results) + await run_input_guardrails_with_queue( + starting_agent, + sequential_guardrails, + ItemHelpers.input_to_new_input_list(prepared_turn_input), + context_wrapper, + streamed_result, + None, + ) + for result in streamed_result.input_guardrail_results[ + existing_input_guardrail_count: + ]: + if result.output.tripwire_triggered: + streamed_result._event_queue.put_nowait(QueueCompleteSentinel()) + session_input_items_for_persistence = ( + await persist_session_items_for_guardrail_trip( + session, + server_conversation_tracker, + session_input_items_for_persistence, + starting_input, + run_state, + store=current_agent.model_settings.resolve( + run_config.model_settings + ).store, + ) + ) + raise InputGuardrailTripwireTriggered(result) + sequential_guardrails = [] + if sandbox_runtime is not None: prepared_sandbox = await sandbox_runtime.prepare_agent( current_agent=current_agent, @@ -848,12 +887,6 @@ async def _save_stream_items_without_count( break if current_turn == 1: - all_input_guardrails = starting_agent.input_guardrails + ( - run_config.input_guardrails or [] - ) - sequential_guardrails = [g for g in all_input_guardrails if not g.run_in_parallel] - parallel_guardrails = [g for g in all_input_guardrails if g.run_in_parallel] - if sequential_guardrails: await run_input_guardrails_with_queue( starting_agent, diff --git a/tests/test_sandbox_runtime.py b/tests/test_sandbox_runtime.py index a3105afa7a..aa047fe96b 100644 --- a/tests/test_sandbox_runtime.py +++ b/tests/test_sandbox_runtime.py @@ -8,6 +8,7 @@ import sys import tempfile import uuid +from collections.abc import Sequence from pathlib import Path from typing import Any, Literal, TypedDict, cast @@ -16,8 +17,8 @@ from openai.types.responses.response_reasoning_item import ResponseReasoningItem, Summary from agents import Agent, AgentHooks, LocalShellTool, RunHooks, Runner, function_tool -from agents.exceptions import UserError -from agents.guardrail import GuardrailFunctionOutput, OutputGuardrail +from agents.exceptions import InputGuardrailTripwireTriggered, UserError +from agents.guardrail import GuardrailFunctionOutput, InputGuardrail, OutputGuardrail from agents.items import ModelResponse, ToolCallOutputItem, TResponseInputItem from agents.model_settings import ModelSettings from agents.prompts import GenerateDynamicPromptData, Prompt @@ -31,8 +32,9 @@ apply_codex_to_manifest, apply_codex_to_session_state, ) -from agents.sandbox.entries import File +from agents.sandbox.entries import BaseEntry, File from agents.sandbox.errors import ExecNonZeroError, ExecTransportError, InvalidManifestPathError +from agents.sandbox.materialization import MaterializedFile from agents.sandbox.runtime import SandboxRuntime from agents.sandbox.runtime_session_manager import SandboxRuntimeSessionManager from agents.sandbox.sandboxes import unix_local as unix_local_module @@ -126,6 +128,34 @@ async def stop(self) -> None: raise RuntimeError("stop failed") +class _LiveSessionDeltaRecorder(_FakeSession): + def __init__(self, manifest: Manifest, *, fail_entry_batch_times: int = 0) -> None: + super().__init__(manifest) + self.apply_manifest_calls = 0 + self.applied_entry_batches: list[list[tuple[Path, BaseEntry]]] = [] + self._fail_entry_batch_times = fail_entry_batch_times + + async def apply_manifest(self, *, only_ephemeral: bool = False): + _ = only_ephemeral + self.apply_manifest_calls += 1 + raise AssertionError("apply_manifest() should not be used for running injected sessions") + + async def _apply_entry_batch( + self, + entries: Sequence[tuple[Path, BaseEntry]], + *, + base_dir: Path, + ) -> list[MaterializedFile]: + _ = base_dir + self.applied_entry_batches.append( + [(dest, artifact.model_copy(deep=True)) for dest, artifact in entries] + ) + if self._fail_entry_batch_times > 0: + self._fail_entry_batch_times -= 1 + raise RuntimeError("delta apply failed") + return [] + + class _BlockingStopSession(_FakeSession): def __init__(self, manifest: Manifest, stop_gate: asyncio.Event) -> None: super().__init__(manifest) @@ -253,6 +283,14 @@ def _extract_user_text(item: dict[str, object]) -> str: raise AssertionError(f"Unexpected content payload: {content!r}") +def _tripwire_input_guardrail( + _context: RunContextWrapper[Any], + _agent: Agent[Any], + _input: str | list[TResponseInputItem], +) -> GuardrailFunctionOutput: + return GuardrailFunctionOutput(output_info=None, tripwire_triggered=True) + + def _get_reasoning_item() -> ResponseReasoningItem: return ResponseReasoningItem( id="rid", @@ -722,6 +760,34 @@ async def test_runner_streamed_cleans_runner_owned_session() -> None: } +@pytest.mark.asyncio +async def test_runner_streamed_guardrail_trip_blocks_runner_owned_sandbox_creation() -> None: + session = _FakeSession(Manifest()) + client = _FakeClient(session) + agent = SandboxAgent( + name="sandbox", + model=FakeModel(initial_output=[get_final_output_message("done")]), + instructions="Base instructions.", + input_guardrails=[ + InputGuardrail( + guardrail_function=_tripwire_input_guardrail, + run_in_parallel=False, + ) + ], + ) + + with pytest.raises(InputGuardrailTripwireTriggered): + result = Runner.run_streamed(agent, "hello", run_config=_sandbox_run_config(client)) + async for _ in result.stream_events(): + pass + + assert client.create_kwargs is None + assert session.start_calls == 0 + assert session.stop_calls == 0 + assert session.shutdown_calls == 0 + assert session.close_dependency_calls == 0 + + @pytest.mark.asyncio async def test_runner_does_not_close_injected_sandbox_session() -> None: model = FakeModel(initial_output=[get_final_output_message("done")]) @@ -821,6 +887,96 @@ async def test_runner_rejects_injected_session_missing_required_codex() -> None: ) +@pytest.mark.asyncio +async def test_runner_guardrail_trip_blocks_runner_owned_sandbox_creation() -> None: + session = _FakeSession(Manifest()) + client = _FakeClient(session) + agent = SandboxAgent( + name="sandbox", + model=FakeModel(initial_output=[get_final_output_message("done")]), + instructions="Base instructions.", + input_guardrails=[ + InputGuardrail( + guardrail_function=_tripwire_input_guardrail, + run_in_parallel=False, + ) + ], + ) + + with pytest.raises(InputGuardrailTripwireTriggered): + await Runner.run(agent, "hello", run_config=_sandbox_run_config(client)) + + assert client.create_kwargs is None + assert session.start_calls == 0 + assert session.stop_calls == 0 + assert session.shutdown_calls == 0 + assert session.close_dependency_calls == 0 + + +@pytest.mark.asyncio +async def test_runner_guardrail_trip_blocks_running_injected_session_mutation() -> None: + live_session = _LiveSessionDeltaRecorder(Manifest()) + live_session._running = True + agent = SandboxAgent( + name="sandbox", + model=FakeModel(initial_output=[get_final_output_message("done")]), + instructions="Base instructions.", + capabilities=[_ManifestMutationCapability()], + input_guardrails=[ + InputGuardrail( + guardrail_function=_tripwire_input_guardrail, + run_in_parallel=False, + ) + ], + ) + + with pytest.raises(InputGuardrailTripwireTriggered): + await Runner.run( + agent, + "hello", + run_config=RunConfig(sandbox=SandboxRunConfig(session=live_session)), + ) + + assert "cap.txt" not in live_session.state.manifest.entries + assert live_session.start_calls == 0 + assert live_session.applied_entry_batches == [] + assert live_session.stop_calls == 0 + assert live_session.shutdown_calls == 0 + + +@pytest.mark.asyncio +async def test_runner_streamed_guardrail_trip_blocks_running_injected_session_mutation() -> None: + live_session = _LiveSessionDeltaRecorder(Manifest()) + live_session._running = True + agent = SandboxAgent( + name="sandbox", + model=FakeModel(initial_output=[get_final_output_message("done")]), + instructions="Base instructions.", + capabilities=[_ManifestMutationCapability()], + input_guardrails=[ + InputGuardrail( + guardrail_function=_tripwire_input_guardrail, + run_in_parallel=False, + ) + ], + ) + + with pytest.raises(InputGuardrailTripwireTriggered): + result = Runner.run_streamed( + agent, + "hello", + run_config=RunConfig(sandbox=SandboxRunConfig(session=live_session)), + ) + async for _ in result.stream_events(): + pass + + assert "cap.txt" not in live_session.state.manifest.entries + assert live_session.start_calls == 0 + assert live_session.applied_entry_batches == [] + assert live_session.stop_calls == 0 + assert live_session.shutdown_calls == 0 + + @pytest.mark.asyncio async def test_runner_uses_public_sandbox_agent_for_dynamic_instructions() -> None: model = FakeModel(initial_output=[get_final_output_message("done")]) From 2ade40df4b04d3e97e9453e51d5fff34551bae13 Mon Sep 17 00:00:00 2001 From: Kazuhiro Sera Date: Mon, 16 Mar 2026 11:16:23 -0700 Subject: [PATCH 06/11] fix: preserve workspace JSONL sink history and runtime-only exclusions (#18) This pull request fixes two correctness bugs in `WorkspaceJsonlSink` by preserving pre-existing outbox history on the first flush and by excluding ephemeral sink outputs through runtime-only persistence paths instead of mutating the manifest. It updates the shared sandbox session layer so Unix-local, Docker, Modal, and E2B persistence all honor the same skip-path set, which keeps durable siblings under existing directories while still pruning the sink outbox. It also adds regressions covering pre-populated outboxes, repeated flushes, existing-parent persistence, Docker staged-copy pruning, and Modal/E2B tar exclusion wiring. --- .../extensions/sandbox/sandboxes/e2b.py | 4 +- .../extensions/sandbox/sandboxes/modal.py | 4 +- src/agents/sandbox/sandboxes/docker.py | 2 +- src/agents/sandbox/sandboxes/unix_local.py | 2 +- .../sandbox/session/base_sandbox_session.py | 18 +++ src/agents/sandbox/session/sinks.py | 38 ++++-- tests/extensions/test_sandbox_e2b.py | 27 ++++ tests/extensions/test_sandbox_modal.py | 46 +++++++ tests/test_sandbox_docker.py | 25 ++++ tests/test_sandbox_session_sinks.py | 117 +++++++++++++++++- 10 files changed, 263 insertions(+), 20 deletions(-) diff --git a/src/agents/extensions/sandbox/sandboxes/e2b.py b/src/agents/extensions/sandbox/sandboxes/e2b.py index 4282493733..aa15b413fe 100644 --- a/src/agents/extensions/sandbox/sandboxes/e2b.py +++ b/src/agents/extensions/sandbox/sandboxes/e2b.py @@ -649,9 +649,7 @@ async def mkdir(self, path: Path | str, *, parents: bool = False) -> None: def _tar_exclude_args(self) -> list[str]: excludes: list[str] = [] - for rel in sorted( - self.state.manifest.ephemeral_persistence_paths(), key=lambda p: p.as_posix() - ): + for rel in sorted(self._persist_workspace_skip_relpaths(), key=lambda p: p.as_posix()): rel_posix = rel.as_posix().lstrip("/") if not rel_posix or rel_posix in {".", "/"}: continue diff --git a/src/agents/extensions/sandbox/sandboxes/modal.py b/src/agents/extensions/sandbox/sandboxes/modal.py index 006e9a0eaa..ba13500813 100644 --- a/src/agents/extensions/sandbox/sandboxes/modal.py +++ b/src/agents/extensions/sandbox/sandboxes/modal.py @@ -498,7 +498,7 @@ async def _persist_workspace_via_snapshot_filesystem(self) -> io.IOBase: # Feature not present in this Modal SDK version; fall back to tar implementation. return await self._persist_workspace_via_tar() - skip = self.state.manifest.ephemeral_persistence_paths() + skip = self._persist_workspace_skip_relpaths() # Modal's snapshot_filesystem does not support excluding paths. To # preserve the semantics of "ephemeral manifest entries are not @@ -626,7 +626,7 @@ def _restore_ephemeral() -> None: async def _persist_workspace_via_tar(self) -> io.IOBase: # Existing tar implementation extracted so snapshot_filesystem mode can fall back cleanly. root = Path(self.state.manifest.root) - skip = self.state.manifest.ephemeral_persistence_paths() + skip = self._persist_workspace_skip_relpaths() excludes: list[str] = [] for rel in sorted(skip, key=lambda p: p.as_posix()): diff --git a/src/agents/sandbox/sandboxes/docker.py b/src/agents/sandbox/sandboxes/docker.py index 45707b04a4..3f47ff855c 100644 --- a/src/agents/sandbox/sandboxes/docker.py +++ b/src/agents/sandbox/sandboxes/docker.py @@ -516,7 +516,7 @@ def _error_context_summary(error: WorkspaceArchiveReadError) -> dict[str, str]: summary["cause"] = str(error.cause) return summary - skip = self.state.manifest.ephemeral_persistence_paths() + skip = self._persist_workspace_skip_relpaths() root = Path(self.state.manifest.root) unmounted_mounts: list[tuple[Mount, Path]] = [] unmount_error: WorkspaceArchiveReadError | None = None diff --git a/src/agents/sandbox/sandboxes/unix_local.py b/src/agents/sandbox/sandboxes/unix_local.py index 5fd2d70a85..fd4475532f 100644 --- a/src/agents/sandbox/sandboxes/unix_local.py +++ b/src/agents/sandbox/sandboxes/unix_local.py @@ -482,7 +482,7 @@ async def persist_workspace(self) -> io.IOBase: path=root, context={"reason": "workspace_root_not_found"} ) - skip = self.state.manifest.ephemeral_persistence_paths() + skip = self._persist_workspace_skip_relpaths() buf = io.BytesIO() try: with tarfile.open(fileobj=buf, mode="w") as tar: diff --git a/src/agents/sandbox/session/base_sandbox_session.py b/src/agents/sandbox/session/base_sandbox_session.py index e6eeb808ea..8802c86012 100644 --- a/src/agents/sandbox/session/base_sandbox_session.py +++ b/src/agents/sandbox/session/base_sandbox_session.py @@ -39,6 +39,7 @@ class BaseSandboxSession(abc.ABC): state: SandboxSessionState _dependencies: Dependencies | None = None _dependencies_closed: bool = False + _runtime_persist_workspace_skip_relpaths: set[Path] | None = None async def start(self) -> None: if await self.state.snapshot.restorable(): @@ -123,6 +124,23 @@ async def _aclose_dependencies(self) -> None: self._dependencies_closed = True await dependencies.aclose() + def _register_persist_workspace_skip_relpath(self, path: Path | str) -> Path: + rel_path = Manifest._coerce_rel_path(path) + Manifest._validate_rel_path(rel_path) + if rel_path in (Path(""), Path(".")): + raise ValueError("Persist workspace skip paths must target a concrete relative path.") + + if self._runtime_persist_workspace_skip_relpaths is None: + self._runtime_persist_workspace_skip_relpaths = set() + self._runtime_persist_workspace_skip_relpaths.add(rel_path) + return rel_path + + def _persist_workspace_skip_relpaths(self) -> set[Path]: + skip_paths = set(self.state.manifest.ephemeral_persistence_paths()) + if self._runtime_persist_workspace_skip_relpaths: + skip_paths.update(self._runtime_persist_workspace_skip_relpaths) + return skip_paths + async def exec( self, *command: str | Path, diff --git a/src/agents/sandbox/session/sinks.py b/src/agents/sandbox/session/sinks.py index 6401ea6d12..f586e7068f 100644 --- a/src/agents/sandbox/session/sinks.py +++ b/src/agents/sandbox/session/sinks.py @@ -10,7 +10,7 @@ from urllib.error import HTTPError, URLError from urllib.request import Request, urlopen -from ..entries import Dir +from ..errors import WorkspaceReadNotFoundError from .base_sandbox_session import BaseSandboxSession from .events import EventPayloadPolicy, UCEvent from .utils import event_to_json_line @@ -180,6 +180,7 @@ def __init__( self._seen = 0 self._lock = asyncio.Lock() self._flush_every = max(1, int(flush_every)) + self._existing_outbox_loaded = False def _resolve_relpath(self) -> Path: rel = self.workspace_relpath @@ -200,16 +201,8 @@ def bind(self, session: BaseSandboxSession) -> None: self._session = _unwrap_session_wrapper(session) self._resolved_workspace_relpath = self._resolve_relpath() if self.ephemeral: - # Mark the parent dir as ephemeral so workspace persistence excludes the outbox. - relpath = self._resolved_workspace_relpath - parent = relpath.parent - key = str(parent) if str(parent) not in ("", ".") else str(relpath) - manifest = self._session.state.manifest - if key not in manifest.entries: - manifest.entries[key] = Dir( - ephemeral=True, - description="sandbox workspace events", - ) + relpath = self._resolved_workspace_relpath or self.workspace_relpath + self._session._register_persist_workspace_skip_relpath(relpath) def _buffer_event(self, event: UCEvent) -> bool: self._buf.extend(event_to_json_line(event).encode("utf-8")) @@ -243,9 +236,32 @@ async def _flush_buffer(self) -> None: if self._session is None: return + await self._ensure_existing_outbox_loaded() relpath = self._resolved_workspace_relpath or self.workspace_relpath await self._session.write(relpath, io.BytesIO(bytes(self._buf))) + async def _ensure_existing_outbox_loaded(self) -> None: + if self._session is None or self._existing_outbox_loaded: + return + + relpath = self._resolved_workspace_relpath or self.workspace_relpath + try: + existing = await self._session.read(relpath) + except (FileNotFoundError, WorkspaceReadNotFoundError): + self._existing_outbox_loaded = True + return + + try: + payload = existing.read() + finally: + existing.close() + + if isinstance(payload, str): + payload = payload.encode("utf-8") + if payload: + self._buf = bytearray(payload) + self._buf + self._existing_outbox_loaded = True + async def handle(self, event: UCEvent) -> None: # If unbound (e.g., Instrumentation.emit used without a SandboxSession wrapper), # no-op. diff --git a/tests/extensions/test_sandbox_e2b.py b/tests/extensions/test_sandbox_e2b.py index f74af4eed1..0ab0a14161 100644 --- a/tests/extensions/test_sandbox_e2b.py +++ b/tests/extensions/test_sandbox_e2b.py @@ -303,6 +303,33 @@ async def test_e2b_persist_workspace_raises_on_nonzero_snapshot_exit() -> None: assert exc_info.value.context["exit_code"] == 2 +@pytest.mark.asyncio +async def test_e2b_persist_workspace_excludes_runtime_skip_paths() -> None: + session, sandbox = _session(workspace_root_ready=True) + sandbox.commands.exec_root_ready = True + session._register_persist_workspace_skip_relpath(Path("logs/events.jsonl")) # noqa: SLF001 + sandbox.commands.next_result = _FakeE2BResult( + stdout=base64.b64encode(b"fake-tar-bytes").decode("ascii") + ) + + archive = await session.persist_workspace() + + assert archive.read() == b"fake-tar-bytes" + expected_command = ( + "tar --exclude=logs/events.jsonl --exclude=./logs/events.jsonl " + "-C /workspace -cf - . | base64 -w0" + ) + assert sandbox.commands.calls == [ + { + "command": expected_command, + "timeout": session.state.timeouts.snapshot_tar_s, + "cwd": "/", + "envs": {}, + "user": None, + } + ] + + @pytest.mark.asyncio async def test_e2b_hydrate_workspace_raises_on_nonzero_extract_exit() -> None: session, sandbox = _session(workspace_root_ready=False) diff --git a/tests/extensions/test_sandbox_modal.py b/tests/extensions/test_sandbox_modal.py index 71c3fa259d..33bf49e0f7 100644 --- a/tests/extensions/test_sandbox_modal.py +++ b/tests/extensions/test_sandbox_modal.py @@ -126,6 +126,52 @@ async def test_modal_stop_is_persistence_only_and_shutdown_terminates( assert await session.running() is False +@pytest.mark.asyncio +async def test_modal_tar_persist_respects_runtime_skip_paths( + monkeypatch: pytest.MonkeyPatch, +) -> None: + modal_module, _create_calls, _registry_tags = _load_modal_module(monkeypatch) + state = modal_module.ModalSandboxSessionState( + manifest=Manifest(root="/workspace"), + snapshot=modal_module.resolve_snapshot(None, "snapshot"), + app_name="sandbox-tests", + sandbox_id="sb-123", + ) + session = modal_module.ModalSandboxSession.from_state(state) + session._register_persist_workspace_skip_relpath(Path("logs/events.jsonl")) # noqa: SLF001 + + commands: list[list[str]] = [] + + async def _fake_exec( + *command: object, + timeout: float | None = None, + shell: bool | list[str] = True, + user: object | None = None, + ) -> ExecResult: + _ = (timeout, shell, user) + rendered = [str(part) for part in command] + commands.append(rendered) + return ExecResult(stdout=b"fake-tar-bytes", stderr=b"", exit_code=0) + + monkeypatch.setattr(session, "exec", _fake_exec) + + archive = await session.persist_workspace() + + assert archive.read() == b"fake-tar-bytes" + assert commands == [ + [ + "tar", + "cf", + "-", + "--exclude", + "./logs/events.jsonl", + "-C", + "/workspace", + ".", + ] + ] + + @pytest.mark.asyncio async def test_modal_snapshot_failure_restores_ephemeral_paths( monkeypatch: pytest.MonkeyPatch, diff --git a/tests/test_sandbox_docker.py b/tests/test_sandbox_docker.py index 92fc64643c..5708df879d 100644 --- a/tests/test_sandbox_docker.py +++ b/tests/test_sandbox_docker.py @@ -311,6 +311,31 @@ async def test_docker_persist_workspace_unmounts_nested_ephemeral_mounts_before_ assert (mount_dir / "remounted.txt").read_text(encoding="utf-8") == "remounted" +@pytest.mark.asyncio +async def test_docker_persist_workspace_prunes_runtime_only_skip_paths_from_staged_copy( + tmp_path: Path, +) -> None: + host_root = tmp_path / "container" + workspace = host_root / "workspace" + logs = workspace / "logs" + logs.mkdir(parents=True) + (logs / "keep.txt").write_text("keep", encoding="utf-8") + (logs / "events.jsonl").write_text("skip", encoding="utf-8") + + session = _HostBackedDockerSession( + host_root=host_root, + manifest=Manifest(root="/workspace"), + ) + session._register_persist_workspace_skip_relpath(Path("logs/events.jsonl")) # noqa: SLF001 + + archive = await session.persist_workspace() + + names = _archive_member_names(archive) + + assert any(name.endswith("workspace/logs/keep.txt") for name in names) + assert not any(name.endswith("workspace/logs/events.jsonl") for name in names) + + @pytest.mark.asyncio async def test_docker_persist_workspace_prunes_explicit_mount_path_from_staged_copy( tmp_path: Path, diff --git a/tests/test_sandbox_session_sinks.py b/tests/test_sandbox_session_sinks.py index 9a0f4560b5..ef74877235 100644 --- a/tests/test_sandbox_session_sinks.py +++ b/tests/test_sandbox_session_sinks.py @@ -9,6 +9,7 @@ import pytest +from agents.sandbox.entries import Dir, File from agents.sandbox.manifest import Manifest from agents.sandbox.sandboxes.unix_local import ( UnixLocalSandboxSession, @@ -30,11 +31,20 @@ from agents.sandbox.snapshot import LocalSnapshot -def _build_unix_local_session(tmp_path: Path) -> UnixLocalSandboxSession: +def _build_unix_local_session( + tmp_path: Path, + *, + manifest: Manifest | None = None, +) -> UnixLocalSandboxSession: workspace = tmp_path / "workspace" snapshot = LocalSnapshot(id=str(uuid.uuid4()), base_path=tmp_path) + session_manifest = ( + manifest.model_copy(update={"root": str(workspace)}, deep=True) + if manifest is not None + else Manifest(root=str(workspace)) + ) state = UnixLocalSandboxSessionState( - manifest=Manifest(root=str(workspace)), + manifest=session_manifest, snapshot=snapshot, ) return UnixLocalSandboxSession.from_state(state) @@ -194,6 +204,109 @@ async def test_workspace_jsonl_sink_supports_session_id_template(tmp_path: Path) assert any(json.loads(line)["op"] == "exec" for line in lines) +@pytest.mark.asyncio +async def test_workspace_jsonl_sink_preserves_preexisting_outbox_contents(tmp_path: Path) -> None: + inner = _build_unix_local_session(tmp_path) + relpath = Path(f"logs/events-{inner.state.session_id}.jsonl") + old_line = b'{"old":true}\n' + + async with inner: + await inner.write(relpath, io.BytesIO(old_line)) + sink = WorkspaceJsonlSink(mode="sync", on_error="raise", ephemeral=False) + sink.bind(inner) + + start = UCStartEvent( + session_id=inner.state.session_id, + seq=1, + op="write", + span_id=uuid.uuid4(), + ) + finish = UCFinishEvent( + session_id=inner.state.session_id, + seq=2, + op="write", + span_id=start.span_id, + ok=True, + duration_ms=0.0, + ) + + await sink.handle(start) + await sink.handle(finish) + + outbox_stream = await inner.read(relpath) + lines = outbox_stream.read().decode("utf-8").splitlines() + + assert len(lines) == 3 + assert json.loads(lines[0]) == {"old": True} + assert json.loads(lines[1])["seq"] == 1 + assert json.loads(lines[2])["seq"] == 2 + + +@pytest.mark.asyncio +async def test_workspace_jsonl_sink_does_not_duplicate_lines_across_flushes( + tmp_path: Path, +) -> None: + inner = _build_unix_local_session(tmp_path) + relpath = Path(f"logs/events-{inner.state.session_id}.jsonl") + + async with inner: + sink = WorkspaceJsonlSink(mode="sync", on_error="raise", ephemeral=False, flush_every=1) + sink.bind(inner) + + for seq in (1, 2, 3): + await sink.handle( + UCStartEvent( + session_id=inner.state.session_id, + seq=seq, + op="write", + span_id=uuid.uuid4(), + ) + ) + + outbox_stream = await inner.read(relpath) + lines = outbox_stream.read().decode("utf-8").splitlines() + + assert [json.loads(line)["seq"] for line in lines] == [1, 2, 3] + + +@pytest.mark.asyncio +async def test_workspace_jsonl_sink_ephemeral_excludes_runtime_outbox_with_existing_parent( + tmp_path: Path, +) -> None: + inner = _build_unix_local_session( + tmp_path, + manifest=Manifest( + entries={ + "logs": Dir( + children={ + "keep.txt": File(content=b"keep"), + } + ) + } + ), + ) + instrumentation = Instrumentation( + sinks=[WorkspaceJsonlSink(mode="sync", on_error="raise", ephemeral=True)] + ) + wrapped = SandboxSession(inner, instrumentation=instrumentation) + + async with wrapped as session: + await session.exec("echo hi") + relpath = Path(f"logs/events-{inner.state.session_id}.jsonl") + outbox_stream = await inner.read(relpath) + assert outbox_stream.read() + + logs_entry = inner.state.manifest.entries["logs"] + assert isinstance(logs_entry, Dir) + assert {str(child) for child in logs_entry.children.keys()} == {"keep.txt"} + + snapshot_path = tmp_path / f"{inner.state.snapshot.id}.tar" + with tarfile.open(snapshot_path, mode="r:*") as tar: + names = [member.name for member in tar.getmembers()] + assert any(name.endswith("logs/keep.txt") for name in names) + assert not any(f"logs/events-{inner.state.session_id}.jsonl" in name for name in names) + + @pytest.mark.asyncio async def test_workspace_jsonl_sink_flushes_on_stop_when_flush_every_gt_one( tmp_path: Path, From 07c5d1c768643ae090f69178d67bbe3b81d3da1a Mon Sep 17 00:00:00 2001 From: Kazuhiro Sera Date: Mon, 16 Mar 2026 11:16:48 -0700 Subject: [PATCH 07/11] fix modal snapshot cleanup failure handling (#16) This pull request fixes Modal snapshot-filesystem persistence so cleanup failures while stripping ephemeral paths fail closed instead of silently taking a snapshot. It checks the pre-snapshot `rm -rf` result before calling `snapshot_filesystem()`, restores the backed-up ephemeral payload when cleanup fails, and raises a structured archive-read error with exit-code context. The change also adds regression coverage for the non-zero cleanup path to ensure snapshotting is skipped and restore still runs. --- .../extensions/sandbox/sandboxes/modal.py | 44 ++++++--- tests/extensions/test_sandbox_modal.py | 99 ++++++++++++++++++- 2 files changed, 127 insertions(+), 16 deletions(-) diff --git a/src/agents/extensions/sandbox/sandboxes/modal.py b/src/agents/extensions/sandbox/sandboxes/modal.py index ba13500813..e4621a063f 100644 --- a/src/agents/extensions/sandbox/sandboxes/modal.py +++ b/src/agents/extensions/sandbox/sandboxes/modal.py @@ -506,20 +506,6 @@ async def _persist_workspace_via_snapshot_filesystem(self) -> io.IOBase: # restore them back into the running session. skip_abs = [root / rel for rel in sorted(skip, key=lambda p: p.as_posix())] ephemeral_backup: bytes | None = None - if skip_abs: - # Best-effort: tar up the ephemeral paths (if they exist). We run - # via shell so missing paths do not cause a hard failure - # (`|| true`). - rel_args = " ".join(shlex.quote(p.relative_to(root).as_posix()) for p in skip_abs) - cmd = f"cd -- {shlex.quote(str(root))} && (tar cf - -- {rel_args} 2>/dev/null || true)" - out = await self.exec("sh", "-lc", cmd, shell=False) - ephemeral_backup = out.stdout or b"" - - # Remove ephemeral paths before snapshot so they are not captured. - rm_cmd = ["rm", "-rf", "--", *[str(p) for p in skip_abs]] - _ = await self.exec(*rm_cmd, shell=False) - - restore_error: WorkspaceArchiveReadError | None = None async def _restore_ephemeral_paths() -> WorkspaceArchiveReadError | None: if not ephemeral_backup: @@ -560,6 +546,36 @@ def _restore_ephemeral() -> None: ) return None + if skip_abs: + # Best-effort: tar up the ephemeral paths (if they exist). We run + # via shell so missing paths do not cause a hard failure + # (`|| true`). + rel_args = " ".join(shlex.quote(p.relative_to(root).as_posix()) for p in skip_abs) + cmd = f"cd -- {shlex.quote(str(root))} && (tar cf - -- {rel_args} 2>/dev/null || true)" + out = await self.exec("sh", "-lc", cmd, shell=False) + ephemeral_backup = out.stdout or b"" + + # Remove ephemeral paths before snapshot so they are not captured. + rm_cmd = ["rm", "-rf", "--", *[str(p) for p in skip_abs]] + rm_out = await self.exec(*rm_cmd, shell=False) + if not rm_out.ok(): + cleanup_restore_error = await _restore_ephemeral_paths() + if cleanup_restore_error is not None: + logger.warning( + "Failed to restore Modal ephemeral paths after cleanup failure: %s", + cleanup_restore_error, + ) + raise WorkspaceArchiveReadError( + path=root, + context={ + "reason": "snapshot_filesystem_ephemeral_remove_failed", + "exit_code": rm_out.exit_code, + "stderr": rm_out.stderr.decode("utf-8", "replace"), + }, + ) + + restore_error: WorkspaceArchiveReadError | None = None + try: snap = await self._call_modal( sandbox.snapshot_filesystem, diff --git a/tests/extensions/test_sandbox_modal.py b/tests/extensions/test_sandbox_modal.py index 33bf49e0f7..b60a33d11f 100644 --- a/tests/extensions/test_sandbox_modal.py +++ b/tests/extensions/test_sandbox_modal.py @@ -179,13 +179,13 @@ async def test_modal_snapshot_failure_restores_ephemeral_paths( modal_module, _create_calls, _registry_tags = _load_modal_module(monkeypatch) class _FakeRestoreProcess: - def __init__(self, owner: _FakeSnapshotSandbox) -> None: + def __init__(self, owner: Any) -> None: self._owner = owner self.stderr = io.BytesIO(b"") self.stdin = self._FakeStdin(owner) class _FakeStdin: - def __init__(self, owner: _FakeSnapshotSandbox) -> None: + def __init__(self, owner: Any) -> None: self._owner = owner self._buffer = bytearray() @@ -262,6 +262,101 @@ async def _fake_call_modal( assert sandbox.restore_payloads == [b"ephemeral-backup"] +@pytest.mark.asyncio +async def test_modal_snapshot_cleanup_failure_raises_before_snapshot( + monkeypatch: pytest.MonkeyPatch, +) -> None: + modal_module, _create_calls, _registry_tags = _load_modal_module(monkeypatch) + + class _FakeRestoreProcess: + def __init__(self, owner: Any) -> None: + self._owner = owner + self.stderr = io.BytesIO(b"") + self.stdin = self._FakeStdin(owner) + + class _FakeStdin: + def __init__(self, owner: Any) -> None: + self._owner = owner + self._buffer = bytearray() + + def write(self, data: bytes) -> None: + self._buffer.extend(data) + + def write_eof(self) -> None: + return + + def drain(self) -> None: + return + + def wait(self) -> int: + self._owner.restore_payloads.append(bytes(self.stdin._buffer)) + return 0 + + class _FakeSnapshotSandbox: + object_id = "sb-123" + + def __init__(self) -> None: + self.restore_payloads: list[bytes] = [] + self.snapshot_calls = 0 + + def snapshot_filesystem(self) -> str: + self.snapshot_calls += 1 + return "snap-123" + + def exec(self, *command: object, **kwargs: object) -> _FakeRestoreProcess: + _ = kwargs + assert command[:3] == ("tar", "xf", "-") + return _FakeRestoreProcess(self) + + sandbox = _FakeSnapshotSandbox() + state = modal_module.ModalSandboxSessionState( + manifest=Manifest( + root="/workspace", + entries={"tmp.txt": File(content=b"ephemeral", ephemeral=True)}, + ), + snapshot=modal_module.resolve_snapshot(None, "snapshot"), + app_name="sandbox-tests", + sandbox_id=sandbox.object_id, + workspace_persistence="snapshot_filesystem", + ) + session = modal_module.ModalSandboxSession.from_state(state, sandbox=sandbox) + + async def _fake_exec( + *command: object, + timeout: float | None = None, + shell: bool | list[str] = True, + user: object | None = None, + ) -> ExecResult: + _ = (timeout, shell, user) + rendered = [str(part) for part in command] + if rendered[:2] == ["sh", "-lc"]: + return ExecResult(stdout=b"ephemeral-backup", stderr=b"", exit_code=0) + if rendered[:3] == ["rm", "-rf", "--"]: + return ExecResult(stdout=b"", stderr=b"rm failed", exit_code=1) + raise AssertionError(f"unexpected command: {rendered!r}") + + async def _fake_call_modal( + fn: Callable[..., object], + *args: object, + call_timeout: float | None = None, + **kwargs: object, + ) -> object: + _ = call_timeout + return fn(*args, **kwargs) + + monkeypatch.setattr(session, "exec", _fake_exec) + monkeypatch.setattr(session, "_call_modal", _fake_call_modal) + + with pytest.raises(WorkspaceArchiveReadError) as exc_info: + await session.persist_workspace() + + assert exc_info.value.context["reason"] == "snapshot_filesystem_ephemeral_remove_failed" + assert exc_info.value.context["exit_code"] == 1 + assert exc_info.value.context["stderr"] == "rm failed" + assert sandbox.snapshot_calls == 0 + assert sandbox.restore_payloads == [b"ephemeral-backup"] + + @pytest.mark.asyncio async def test_modal_snapshot_filesystem_uses_resolved_mount_paths_for_backup_and_removal( monkeypatch: pytest.MonkeyPatch, From dcf329e6daa5aa6ceed909a06a5c67145a0e3b64 Mon Sep 17 00:00:00 2001 From: Kazuhiro Sera Date: Mon, 16 Mar 2026 11:17:23 -0700 Subject: [PATCH 08/11] fix: process capability manifests for injected sandbox sessions (#17) This pull request fixes a behavioral mismatch where `SandboxRunConfig(session=...)` skipped `Capability.process_manifest()` while `session_state` resume flows and fresh `client.create(...)` sessions did not. In practice, that meant capability-provided manifest changes only worked in some sandbox startup modes. A capability that adds workspace scaffolding such as `README.md`, `cap.txt`, helper files, or other manifest-backed setup would behave correctly when the runtime created or resumed a session, but silently fail when a caller injected an already-created live session. The same agent and capability could therefore produce different instructions, different visible workspace files, and different tool preconditions depending only on whether the run used `session=...` or `session_state=...`. This was especially risky for long-lived injected sessions. Capability tools still bound to the session, but any files or manifest-backed instructions those tools expected were never added to `session.state.manifest`, and for already-running sessions they were never materialized into the workspace either. That creates a hard-to-diagnose failure mode where capability-dependent runs break only in the live-session configuration. This change makes injected live sessions follow the same manifest-processing path as the other sandbox acquisition modes. The runtime now applies capability manifest mutations to the injected session state, reapplies the manifest once for already-running injected sessions so capability-owned files exist without restarting the session, and preserves the existing caller-owned lifecycle semantics for injected sessions. --- src/agents/sandbox/runtime_session_manager.py | 199 ++++++++++++++++ tests/test_sandbox_runtime.py | 219 ++++++++++++++++++ 2 files changed, 418 insertions(+) diff --git a/src/agents/sandbox/runtime_session_manager.py b/src/agents/sandbox/runtime_session_manager.py index 50a100c1b5..096c5fc54d 100644 --- a/src/agents/sandbox/runtime_session_manager.py +++ b/src/agents/sandbox/runtime_session_manager.py @@ -4,6 +4,7 @@ import copy import threading from dataclasses import dataclass, field +from pathlib import Path from typing import Any, Generic, cast from ..agent import Agent @@ -17,6 +18,7 @@ ) from .capabilities import Capability from .codex_config import manifest_has_codex_entry +from .entries import BaseEntry, Dir, Mount, resolve_workspace_path from .manifest import Manifest from .sandbox_agent import SandboxAgent from .session.base_sandbox_session import BaseSandboxSession @@ -100,6 +102,12 @@ class _SandboxConcurrencyGuard: active_runs: int = 0 +@dataclass(frozen=True) +class _LiveSessionManifestUpdate: + processed_manifest: Manifest | None + entries_to_apply: list[tuple[Path, BaseEntry]] + + class SandboxRuntimeSessionManager(Generic[TContext]): def __init__( self, @@ -256,6 +264,21 @@ async def _create_resources( sandbox_config = self._require_sandbox_config() if sandbox_config.session is not None: self._validate_injected_session(agent=agent, session=sandbox_config.session) + running = await sandbox_config.session.running() + manifest_update = self._process_live_session_manifest( + capabilities=capabilities, + session=sandbox_config.session, + running=running, + ) + if manifest_update.entries_to_apply: + await sandbox_config.session._apply_entry_batch( + manifest_update.entries_to_apply, + base_dir=sandbox_config.session._manifest_base_dir(), + ) + if manifest_update.processed_manifest is not None: + sandbox_config.session.state = sandbox_config.session.state.model_copy( + update={"manifest": manifest_update.processed_manifest} + ) return _SandboxSessionResources( session=sandbox_config.session, client=None, @@ -416,6 +439,182 @@ def _process_manifest( processed_manifest = capability.process_manifest(processed_manifest) return processed_manifest + @classmethod + def _process_live_session_manifest( + cls, + *, + capabilities: list[Capability], + session: BaseSandboxSession, + running: bool, + ) -> _LiveSessionManifestUpdate: + current_manifest = session.state.manifest + processed_manifest = cls._process_manifest(capabilities, current_manifest) + if processed_manifest is None or processed_manifest == current_manifest: + return _LiveSessionManifestUpdate(processed_manifest=None, entries_to_apply=[]) + + entries_to_apply: list[tuple[Path, BaseEntry]] = [] + if running: + cls._validate_running_live_session_manifest_update( + current_manifest=current_manifest, + processed_manifest=processed_manifest, + ) + entries_to_apply = cls._diff_live_session_entries( + current_entries=current_manifest.entries, + processed_entries=processed_manifest.entries, + ) + entries_to_apply = [ + ( + resolve_workspace_path(Path(processed_manifest.root), rel_path), + artifact, + ) + for rel_path, artifact in entries_to_apply + ] + + return _LiveSessionManifestUpdate( + processed_manifest=processed_manifest, + entries_to_apply=entries_to_apply, + ) + + @classmethod + def _validate_running_live_session_manifest_update( + cls, + *, + current_manifest: Manifest, + processed_manifest: Manifest, + ) -> None: + if processed_manifest.root != current_manifest.root: + raise ValueError( + "Running injected sandbox sessions do not support capability changes to " + "`manifest.root`; use a fresh session or a session_state resume flow." + ) + if processed_manifest.environment != current_manifest.environment: + raise ValueError( + "Running injected sandbox sessions do not support capability changes to " + "`manifest.environment`; use a fresh session or a session_state resume flow." + ) + if ( + processed_manifest.users != current_manifest.users + or processed_manifest.groups != current_manifest.groups + ): + raise ValueError( + "Running injected sandbox sessions do not support capability changes to " + "`manifest.users` or `manifest.groups`; use a fresh session or a " + "session_state resume flow." + ) + + @classmethod + def _diff_live_session_entries( + cls, + *, + current_entries: dict[str | Path, BaseEntry], + processed_entries: dict[str | Path, BaseEntry], + parent_rel: Path = Path(), + ) -> list[tuple[Path, BaseEntry]]: + current_by_name = { + Manifest._coerce_rel_path(name): entry for name, entry in current_entries.items() + } + processed_by_name = { + Manifest._coerce_rel_path(name): entry for name, entry in processed_entries.items() + } + + removed = sorted(current_by_name.keys() - processed_by_name.keys()) + if removed: + removed_paths = ", ".join((parent_rel / rel).as_posix() for rel in removed) + raise ValueError( + "Running injected sandbox sessions do not support removing manifest entries: " + f"{removed_paths}." + ) + + entries_to_apply: list[tuple[Path, BaseEntry]] = [] + for rel_name, processed_entry in processed_by_name.items(): + rel_path = parent_rel / rel_name + current_entry = current_by_name.get(rel_name) + if current_entry is None: + cls._validate_running_live_session_entry_addition( + rel_path=rel_path, + entry=processed_entry, + ) + entries_to_apply.append((rel_path, processed_entry.model_copy(deep=True))) + continue + + delta_entry = cls._diff_live_session_entry( + rel_path=rel_path, + current_entry=current_entry, + processed_entry=processed_entry, + ) + if delta_entry is not None: + entries_to_apply.append((rel_path, delta_entry)) + + return entries_to_apply + + @classmethod + def _diff_live_session_entry( + cls, + *, + rel_path: Path, + current_entry: BaseEntry, + processed_entry: BaseEntry, + ) -> BaseEntry | None: + if current_entry == processed_entry: + return None + + if type(current_entry) is not type(processed_entry) or ( + current_entry.is_dir != processed_entry.is_dir + ): + raise ValueError( + "Running injected sandbox sessions do not support replacing manifest entry " + f"types at {rel_path.as_posix()}; use a fresh session or a session_state " + "resume flow." + ) + + if isinstance(current_entry, Mount): + raise ValueError( + "Running injected sandbox sessions do not support capability changes to mount " + f"entries at {rel_path.as_posix()}; use a fresh session or a session_state " + "resume flow." + ) + + if isinstance(current_entry, Dir) and isinstance(processed_entry, Dir): + changed_children = dict( + cls._diff_live_session_entries( + current_entries=current_entry.children, + processed_entries=processed_entry.children, + parent_rel=Path(), + ) + ) + metadata_changed = current_entry.model_dump( + exclude={"children"} + ) != processed_entry.model_dump(exclude={"children"}) + if not metadata_changed and not changed_children: + return None + return processed_entry.model_copy(update={"children": changed_children}, deep=True) + + return processed_entry.model_copy(deep=True) + + @staticmethod + def _validate_running_live_session_entry_addition( + *, + rel_path: Path, + entry: BaseEntry, + ) -> None: + if SandboxRuntimeSessionManager._entry_contains_mount(entry): + raise ValueError( + "Running injected sandbox sessions do not support capability-added mount " + f"entries at {rel_path.as_posix()}; use a fresh session or a session_state " + "resume flow." + ) + + @staticmethod + def _entry_contains_mount(entry: BaseEntry) -> bool: + if isinstance(entry, Mount): + return True + if isinstance(entry, Dir): + return any( + SandboxRuntimeSessionManager._entry_contains_mount(child) + for child in entry.children.values() + ) + return False + @classmethod def _process_resumed_state_manifest( cls, diff --git a/tests/test_sandbox_runtime.py b/tests/test_sandbox_runtime.py index aa047fe96b..e70b3f067b 100644 --- a/tests/test_sandbox_runtime.py +++ b/tests/test_sandbox_runtime.py @@ -511,6 +511,17 @@ def process_manifest(self, manifest: Manifest) -> Manifest: return manifest +class _ManifestUsersCapability(Capability): + type: str = "manifest-users" + + def __init__(self) -> None: + super().__init__(type="manifest-users") + + def process_manifest(self, manifest: Manifest) -> Manifest: + manifest.users.append(User(name="sandbox-user")) + return manifest + + class _SessionFileCapability(Capability): type: str = "session-files" bound_session: BaseSandboxSession | None = None @@ -2098,6 +2109,214 @@ async def test_session_manager_reapplies_capability_manifest_mutations_on_resume assert client.resume_state.manifest.entries["cap.txt"] == File(content=b"capability") +@pytest.mark.asyncio +@pytest.mark.parametrize("source", ["live_session", "session_state", "create"]) +async def test_session_manager_applies_capability_manifest_mutations_with_session_parity( + source: str, +) -> None: + capability = _ManifestMutationCapability() + agent = SandboxAgent(name="worker", model=FakeModel(), instructions="Worker.", codex=False) + run_state: RunState[Any, Agent[Any]] | None = None + + if source == "live_session": + live_session = _FakeSession(Manifest()) + sandbox_config = SandboxRunConfig(session=live_session) + else: + client = _FakeClient(_FakeSession(Manifest())) + if source == "session_state": + sandbox_config = SandboxRunConfig( + client=client, + session_state=SandboxSessionState( + manifest=Manifest(), + snapshot=NoopSnapshot(id="resume"), + ), + options={"image": "sandbox"}, + ) + else: + sandbox_config = SandboxRunConfig( + client=client, + manifest=Manifest(), + options={"image": "sandbox"}, + ) + + manager = SandboxRuntimeSessionManager( + starting_agent=agent, + sandbox_config=sandbox_config, + run_state=run_state, + ) + + manager.acquire_agent(agent) + session = await manager.ensure_session( + agent=agent, + capabilities=[capability], + is_resumed_state=False, + ) + + assert session.state.manifest.entries["cap.txt"] == File(content=b"capability") + if source == "session_state": + assert client.resume_state is not None + assert client.resume_state.manifest.entries["cap.txt"] == File(content=b"capability") + if source == "create": + assert client.create_kwargs is not None + manifest = client.create_kwargs["manifest"] + assert manifest is not None + assert manifest.entries["cap.txt"] == File(content=b"capability") + + +@pytest.mark.asyncio +async def test_session_manager_starts_stopped_injected_session_with_manifest_mutation() -> None: + live_session = _LiveSessionDeltaRecorder(Manifest()) + capability = _ManifestMutationCapability() + agent = SandboxAgent(name="worker", model=FakeModel(), instructions="Worker.", codex=False) + manager = SandboxRuntimeSessionManager( + starting_agent=agent, + sandbox_config=SandboxRunConfig(session=live_session), + run_state=None, + ) + + manager.acquire_agent(agent) + session = await manager.ensure_session( + agent=agent, + capabilities=[capability], + is_resumed_state=False, + ) + payload = await manager.cleanup() + + assert session is live_session + assert live_session.start_calls == 1 + assert live_session.apply_manifest_calls == 0 + assert live_session.stop_calls == 0 + assert live_session.shutdown_calls == 0 + assert session.state.manifest.entries["cap.txt"] == File(content=b"capability") + assert payload is None + + +@pytest.mark.asyncio +async def test_session_manager_materializes_running_injected_session_manifest_mutation() -> None: + live_session = _LiveSessionDeltaRecorder(Manifest()) + live_session._running = True + capability = _ManifestMutationCapability() + agent = SandboxAgent(name="worker", model=FakeModel(), instructions="Worker.", codex=False) + manager = SandboxRuntimeSessionManager( + starting_agent=agent, + sandbox_config=SandboxRunConfig(session=live_session), + run_state=None, + ) + + manager.acquire_agent(agent) + session = await manager.ensure_session( + agent=agent, + capabilities=[capability], + is_resumed_state=False, + ) + payload = await manager.cleanup() + + assert session is live_session + assert live_session.start_calls == 0 + assert live_session.apply_manifest_calls == 0 + assert live_session.applied_entry_batches == [ + [(Path("/workspace/cap.txt"), File(content=b"capability"))] + ] + assert session.state.manifest.entries["cap.txt"] == File(content=b"capability") + assert live_session.stop_calls == 0 + assert live_session.shutdown_calls == 0 + assert payload is None + + +@pytest.mark.asyncio +async def test_session_manager_retries_running_injected_session_delta_apply_after_failure() -> None: + live_session = _LiveSessionDeltaRecorder(Manifest(), fail_entry_batch_times=1) + live_session._running = True + capability = _ManifestMutationCapability() + agent = SandboxAgent(name="worker", model=FakeModel(), instructions="Worker.", codex=False) + manager = SandboxRuntimeSessionManager( + starting_agent=agent, + sandbox_config=SandboxRunConfig(session=live_session), + run_state=None, + ) + + manager.acquire_agent(agent) + with pytest.raises(RuntimeError, match="delta apply failed"): + await manager.ensure_session( + agent=agent, + capabilities=[capability], + is_resumed_state=False, + ) + + assert live_session.state.manifest.entries == {} + assert live_session.applied_entry_batches == [ + [(Path("/workspace/cap.txt"), File(content=b"capability"))] + ] + + session = await manager.ensure_session( + agent=agent, + capabilities=[capability], + is_resumed_state=False, + ) + payload = await manager.cleanup() + + assert session is live_session + assert live_session.state.manifest.entries["cap.txt"] == File(content=b"capability") + assert live_session.applied_entry_batches == [ + [(Path("/workspace/cap.txt"), File(content=b"capability"))], + [(Path("/workspace/cap.txt"), File(content=b"capability"))], + ] + assert payload is None + + +@pytest.mark.asyncio +async def test_session_manager_skips_rematerialization_for_unchanged_running_session() -> None: + live_session = _LiveSessionDeltaRecorder(Manifest()) + live_session._running = True + agent = SandboxAgent(name="worker", model=FakeModel(), instructions="Worker.", codex=False) + manager = SandboxRuntimeSessionManager( + starting_agent=agent, + sandbox_config=SandboxRunConfig(session=live_session), + run_state=None, + ) + + manager.acquire_agent(agent) + session = await manager.ensure_session( + agent=agent, + capabilities=[Capability(type="noop")], + is_resumed_state=False, + ) + payload = await manager.cleanup() + + assert session is live_session + assert live_session.start_calls == 0 + assert live_session.apply_manifest_calls == 0 + assert live_session.applied_entry_batches == [] + assert session.state.manifest.entries == {} + assert live_session.stop_calls == 0 + assert live_session.shutdown_calls == 0 + assert payload is None + + +@pytest.mark.asyncio +async def test_session_manager_rejects_running_injected_session_account_mutation() -> None: + live_session = _LiveSessionDeltaRecorder(Manifest()) + live_session._running = True + agent = SandboxAgent(name="worker", model=FakeModel(), instructions="Worker.", codex=False) + manager = SandboxRuntimeSessionManager( + starting_agent=agent, + sandbox_config=SandboxRunConfig(session=live_session), + run_state=None, + ) + + manager.acquire_agent(agent) + with pytest.raises(ValueError, match="manifest.users` or `manifest.groups"): + await manager.ensure_session( + agent=agent, + capabilities=[_ManifestUsersCapability()], + is_resumed_state=False, + ) + + assert live_session.apply_manifest_calls == 0 + assert live_session.applied_entry_batches == [] + assert live_session.state.manifest.users == [] + + @pytest.mark.asyncio async def test_session_manager_preserves_existing_payload_when_no_sandbox_session_is_used() -> None: client = _FakeClient(_FakeSession(Manifest())) From e8b77ec832c589a56adf9f8ffe16aeb8244dfe5e Mon Sep 17 00:00:00 2001 From: Kazuhiro Sera Date: Mon, 16 Mar 2026 11:19:38 -0700 Subject: [PATCH 09/11] fix: harden sandbox zip stream compatibility (#14) This pull request fixes sandbox ZIP extraction compatibility for helper-call paths that receive non-seekable archive streams. The change replaces the old `seekable()`-presence heuristic with an actual random-access probe in `src/agents/sandbox/session/archive_extraction.py`. Streams that already support `tell()` and `seek()` are passed through unchanged, while non-seekable streams are copied into a rewindable `SpooledTemporaryFile` before `zipfile.ZipFile(...)` reads them. This keeps the normal `BaseSandboxSession.extract()` behavior unchanged while making the lower-level ZIP helper correct for future direct callers. The pull request also removes the now-unused private `_zipfile_compatible_stream()` seam from `src/agents/sandbox/session/base_sandbox_session.py` and updates `tests/test_sandbox_extract.py` to cover both valid random-access streams without a `seekable()` method and streams whose `seekable()` method explicitly returns `False`. --- .../sandbox/session/archive_extraction.py | 101 +++++++++++------- .../sandbox/session/base_sandbox_session.py | 5 - tests/test_sandbox_extract.py | 29 ++++- 3 files changed, 87 insertions(+), 48 deletions(-) diff --git a/src/agents/sandbox/session/archive_extraction.py b/src/agents/sandbox/session/archive_extraction.py index ca219661d2..6bf5dc09ac 100644 --- a/src/agents/sandbox/session/archive_extraction.py +++ b/src/agents/sandbox/session/archive_extraction.py @@ -1,9 +1,12 @@ from __future__ import annotations import io +import shutil import tarfile +import tempfile import zipfile -from collections.abc import Awaitable, Callable +from collections.abc import Awaitable, Callable, Iterator +from contextlib import contextmanager from pathlib import Path, PurePosixPath from typing import Literal, cast @@ -107,45 +110,46 @@ async def extract_zip_archive( ) -> None: child_entry_cache: dict[Path, dict[str, EntryKind]] = {} try: - with zipfile.ZipFile(zipfile_compatible_stream(data)) as archive: - for member in archive.infolist(): - rel_path = safe_zip_member_rel_path(member) - if rel_path is None: - continue + with zipfile_compatible_stream(data) as zip_data: + with zipfile.ZipFile(zip_data) as archive: + for member in archive.infolist(): + rel_path = safe_zip_member_rel_path(member) + if rel_path is None: + continue + + await self._ensure_no_symlink_extract_parents( + destination_root=destination_root, + rel_path=rel_path, + member_name=member.filename, + error_type="zip", + child_entry_cache=child_entry_cache, + ) + dest = destination_root / rel_path + if member.is_dir(): + await self._mkdir(dest) + self._record_extract_entry( + child_entry_cache=child_entry_cache, + destination_root=destination_root, + path=dest, + kind=EntryKind.DIRECTORY, + ) + continue - await self._ensure_no_symlink_extract_parents( - destination_root=destination_root, - rel_path=rel_path, - member_name=member.filename, - error_type="zip", - child_entry_cache=child_entry_cache, - ) - dest = destination_root / rel_path - if member.is_dir(): - await self._mkdir(dest) + await self._mkdir(dest.parent) self._record_extract_entry( child_entry_cache=child_entry_cache, destination_root=destination_root, - path=dest, + path=dest.parent, kind=EntryKind.DIRECTORY, ) - continue - - await self._mkdir(dest.parent) - self._record_extract_entry( - child_entry_cache=child_entry_cache, - destination_root=destination_root, - path=dest.parent, - kind=EntryKind.DIRECTORY, - ) - with archive.open(member, mode="r") as member_data: - await self._write(dest, cast(io.IOBase, member_data)) - self._record_extract_entry( - child_entry_cache=child_entry_cache, - destination_root=destination_root, - path=dest, - kind=EntryKind.FILE, - ) + with archive.open(member, mode="r") as member_data: + await self._write(dest, cast(io.IOBase, member_data)) + self._record_extract_entry( + child_entry_cache=child_entry_cache, + destination_root=destination_root, + path=dest, + kind=EntryKind.FILE, + ) except UnsafeZipMemberError as e: raise WorkspaceArchiveWriteError( path=archive_path, @@ -249,11 +253,28 @@ def _record_extract_entry( current_dir = current_dir / part -def zipfile_compatible_stream(stream: io.IOBase) -> io.IOBase: - seekable = getattr(stream, "seekable", None) - if callable(seekable): - return stream - return _ZipFileStreamAdapter(stream) +def _supports_zip_random_access(stream: io.IOBase) -> bool: + try: + position = stream.tell() + stream.seek(position, io.SEEK_SET) + except (AttributeError, OSError, TypeError, ValueError): + return False + return True + + +@contextmanager +def zipfile_compatible_stream(stream: io.IOBase) -> Iterator[io.IOBase]: + if _supports_zip_random_access(stream): + yield _ZipFileStreamAdapter(stream) + return + + spool = tempfile.SpooledTemporaryFile(max_size=16 * 1024 * 1024, mode="w+b") + try: + shutil.copyfileobj(stream, spool) + spool.seek(0) + yield _ZipFileStreamAdapter(cast(io.IOBase, spool)) + finally: + spool.close() def safe_zip_member_rel_path(member: zipfile.ZipInfo) -> Path | None: @@ -274,6 +295,8 @@ def safe_zip_member_rel_path(member: zipfile.ZipInfo) -> Path | None: class _ZipFileStreamAdapter(io.IOBase): + # Python 3.10's zipfile._SharedFile reads `file.seekable` directly, so this + # adapter keeps ZIP-compatible random-access streams working across versions. def __init__(self, stream: io.IOBase) -> None: self._stream = stream diff --git a/src/agents/sandbox/session/base_sandbox_session.py b/src/agents/sandbox/session/base_sandbox_session.py index 8802c86012..d13ba228f5 100644 --- a/src/agents/sandbox/session/base_sandbox_session.py +++ b/src/agents/sandbox/session/base_sandbox_session.py @@ -28,7 +28,6 @@ from .archive_extraction import ( WorkspaceArchiveExtractor, safe_zip_member_rel_path, - zipfile_compatible_stream, ) from .dependencies import Dependencies from .manifest_application import ManifestApplier @@ -420,10 +419,6 @@ async def _extract_zip_archive( data=data, ) - @staticmethod - def _zipfile_compatible_stream(stream: io.IOBase) -> io.IOBase: - return zipfile_compatible_stream(stream) - @staticmethod def _safe_zip_member_rel_path(member) -> Path | None: return safe_zip_member_rel_path(member) diff --git a/tests/test_sandbox_extract.py b/tests/test_sandbox_extract.py index 5f7d105a88..93c6fca1b5 100644 --- a/tests/test_sandbox_extract.py +++ b/tests/test_sandbox_extract.py @@ -16,6 +16,7 @@ UnixLocalSandboxSession, UnixLocalSandboxSessionState, ) +from agents.sandbox.session.archive_extraction import zipfile_compatible_stream from agents.sandbox.session.base_sandbox_session import BaseSandboxSession from agents.sandbox.snapshot import NoopSnapshot from agents.sandbox.types import ExecResult, Permissions @@ -193,13 +194,33 @@ def read(self, size: int = -1) -> bytes: return bytes(out) -def test_zipfile_compatible_stream_wraps_streams_without_seekable() -> None: +class _SeekableFalseZipStream(io.IOBase): + def __init__(self, payload: bytes) -> None: + self._buffer = io.BytesIO(payload) + + def seekable(self) -> bool: + return False + + def read(self, size: int = -1) -> bytes: + return self._buffer.read(size) + + +def test_zipfile_compatible_stream_supports_streams_without_seekable() -> None: raw_stream = _NoSeekableZipStream(_zip_bytes(members={"file.txt": b"hello"}).getvalue()) - compatible = BaseSandboxSession._zipfile_compatible_stream(raw_stream) + with zipfile_compatible_stream(raw_stream) as compatible: + assert compatible.seekable() is True + with zipfile.ZipFile(compatible) as archive: + assert archive.read("file.txt") == b"hello" + + +def test_zipfile_compatible_stream_buffers_streams_with_seekable_false() -> None: + raw_stream = _SeekableFalseZipStream(_zip_bytes(members={"file.txt": b"hello"}).getvalue()) - with zipfile.ZipFile(compatible) as archive: - assert archive.read("file.txt") == b"hello" + with zipfile_compatible_stream(raw_stream) as compatible: + assert compatible.seekable() is True + with zipfile.ZipFile(compatible) as archive: + assert archive.read("file.txt") == b"hello" @pytest.mark.asyncio From 600e461ab2c922fe8468f4139ab87cb5fa6494c1 Mon Sep 17 00:00:00 2001 From: Steve Coffey Date: Mon, 16 Mar 2026 16:47:07 -0700 Subject: [PATCH 10/11] feat: add sandbox skills capability and tax prep demo (#20) This pull request adds a sandbox `Skills` capability and ports the tax-prep packaged-agent demo onto the sandbox runtime used in this repository. This branch includes four follow-up commits: - `f25d142f` fixes Modal archive writes and hardens `ls` path parsing. It also renames the Docker runner example to `examples/sandbox/basic.py`, updates the extensions README, and adds regression coverage. - `62a37eda` makes sandbox Codex installs ephemeral by default so Codex artifacts are treated as runtime-only state. - `3cb9e996` adds the new skills capability, focused tests, a sandbox-backed apply-patch helper for examples, and a Docker-based `examples/sandbox/tax_prep.py` demo with sample tax PDFs. - `88e45e3e` thins the sandbox skills capability so it only mounts skills into a Codex auto-discovery root (defaulting to `.agents/skills`) and no longer injects prompt-side skill indexes or custom skill instructions. Notes: - I intentionally left unrelated local files uncommitted, including `2026-03-16__victoria_zheng/`. - The full local verification stack now passes: `make format`, `make lint`, `make typecheck`, and `make tests`. --- .../sandbox/{docker_runner.py => basic.py} | 138 +++++++-- examples/sandbox/data/f1040.pdf | Bin 0 -> 220237 bytes examples/sandbox/data/sample_w2.pdf | Bin 0 -> 1466312 bytes examples/sandbox/extensions/README.md | 11 + .../sandbox/misc/workspace_apply_patch.py | 78 +++++ examples/sandbox/tax_prep.py | 264 +++++++++++++++++ .../extensions/sandbox/sandboxes/modal.py | 26 +- src/agents/sandbox/capabilities/__init__.py | 3 +- src/agents/sandbox/capabilities/skills.py | 270 ++++++++++++++++++ src/agents/sandbox/codex_config.py | 2 +- src/agents/sandbox/entries/codex.py | 1 + src/agents/sandbox/errors.py | 20 ++ src/agents/sandbox/util/parse_utils.py | 7 +- tests/extensions/test_sandbox_modal.py | 187 ++++++++++++ tests/test_example_workflows.py | 2 +- tests/test_sandbox_manifest.py | 10 + tests/test_sandbox_parse_utils.py | 26 ++ tests/test_sandbox_runtime.py | 2 +- tests/test_sandbox_skills_capability.py | 129 +++++++++ 19 files changed, 1131 insertions(+), 45 deletions(-) rename examples/sandbox/{docker_runner.py => basic.py} (53%) create mode 100644 examples/sandbox/data/f1040.pdf create mode 100644 examples/sandbox/data/sample_w2.pdf create mode 100644 examples/sandbox/misc/workspace_apply_patch.py create mode 100644 examples/sandbox/tax_prep.py create mode 100644 src/agents/sandbox/capabilities/skills.py create mode 100644 tests/test_sandbox_parse_utils.py create mode 100644 tests/test_sandbox_skills_capability.py diff --git a/examples/sandbox/docker_runner.py b/examples/sandbox/basic.py similarity index 53% rename from examples/sandbox/docker_runner.py rename to examples/sandbox/basic.py index 64778d2e10..2b8fe00e3d 100644 --- a/examples/sandbox/docker_runner.py +++ b/examples/sandbox/basic.py @@ -1,33 +1,30 @@ -""" -Start here if you are new to Docker-backed sandbox examples. - -This file keeps the flow explicit: - -1. Build a manifest for the files that should appear in the sandbox workspace. -2. Create a sandbox agent that can inspect that workspace through one shell tool. -3. Start a Docker-backed sandbox session, stream the run, and print what happens. -""" +from __future__ import annotations import argparse import asyncio import sys from pathlib import Path +from typing import Any, Literal, cast -from docker import from_env as docker_from_env # type: ignore[import-untyped] from openai.types.responses import ResponseTextDeltaEvent from agents import ModelSettings, Runner from agents.run import RunConfig from agents.sandbox import Manifest, SandboxAgent, SandboxRunConfig from agents.sandbox.entries import File -from agents.sandbox.sandboxes.docker import DockerSandboxClient, DockerSandboxClientOptions if __package__ is None or __package__ == "": sys.path.insert(0, str(Path(__file__).resolve().parents[2])) from examples.sandbox.misc.workspace_shell import WorkspaceShellCapability +Backend = Literal["docker", "modal"] +WorkspacePersistenceMode = Literal["tar", "snapshot_filesystem"] + DEFAULT_QUESTION = "Summarize this sandbox project in 2 sentences." +DEFAULT_BACKEND: Backend = "docker" +DEFAULT_MODAL_APP_NAME = "openai-agents-python-sandbox-example" +DEFAULT_MODAL_WORKSPACE_PERSISTENCE: WorkspacePersistenceMode = "tar" def _stream_event_banner(event_name: str) -> str | None: @@ -38,16 +35,18 @@ def _stream_event_banner(event_name: str) -> str | None: return None -async def main(model: str, question: str) -> None: - # A manifest is the starting file tree for the sandbox workspace. - # Each key is a path inside the workspace and each value is a concrete manifest entry. - manifest = Manifest( +def _build_manifest(backend: Backend) -> Manifest: + backend_label = "Docker" if backend == "docker" else "Modal" + return Manifest( entries={ "README.md": File( content=( b"# Demo Project\n\n" - b"This sandbox contains a tiny demo project for the sandbox runner.\n" - b"The goal is to show how Runner can prepare a Docker-backed workspace.\n" + + ( + f"This sandbox contains a tiny demo project for the {backend_label} " + "sandbox runner.\n" + ).encode() + + b"The goal is to show how Runner can prepare a sandbox workspace.\n" ) ), "src/app.py": File( @@ -63,8 +62,11 @@ async def main(model: str, question: str) -> None: } ) - agent = SandboxAgent( - name="Docker Sandbox Assistant", + +def _build_agent(*, model: str, manifest: Manifest, backend: Backend) -> SandboxAgent: + backend_label = "Docker" if backend == "docker" else "Modal" + return SandboxAgent( + name=f"{backend_label} Sandbox Assistant", model=model, # `instructions` is the base agent instructions for this example's task. instructions=( @@ -86,19 +88,79 @@ async def main(model: str, question: str) -> None: model_settings=ModelSettings(tool_choice="required"), ) - # The Docker client owns the container lifecycle for the sandbox session. - docker_client = DockerSandboxClient(docker_from_env()) - # `create()` allocates a fresh sandbox session backed by a Docker container. - # We pass the same manifest here so the container knows which files to materialize. - session = await docker_client.create( +def _require_modal_dependency() -> tuple[Any, Any]: + try: + from agents.extensions.sandbox import ModalSandboxClient, ModalSandboxClientOptions + except Exception as exc: # pragma: no cover - import path depends on optional extras + raise SystemExit( + "Modal-backed runs require the optional repo extra.\n" + "Install it with: uv sync --extra modal" + ) from exc + + return ModalSandboxClient, ModalSandboxClientOptions + + +def _require_docker_dependency() -> tuple[Any, Any, Any]: + try: + from docker import from_env as docker_from_env # type: ignore[import-untyped] + except Exception as exc: # pragma: no cover - import path depends on local Docker setup + raise SystemExit( + "Docker-backed runs require the Docker SDK.\n" + "Install the repo dependencies with: make sync" + ) from exc + + from agents.sandbox.sandboxes.docker import DockerSandboxClient, DockerSandboxClientOptions + + return docker_from_env, DockerSandboxClient, DockerSandboxClientOptions + + +async def _create_session( + *, + backend: Backend, + manifest: Manifest, + agent: SandboxAgent, +): + if backend == "docker": + docker_from_env, DockerSandboxClient, DockerSandboxClientOptions = ( + _require_docker_dependency() + ) + client = DockerSandboxClient(docker_from_env()) + session = await client.create( + manifest=manifest, + codex=agent.codex, + options=DockerSandboxClientOptions(image="python:3.14-slim"), + ) + return client, session + + ModalSandboxClient, ModalSandboxClientOptions = _require_modal_dependency() + client = ModalSandboxClient() + session = await client.create( manifest=manifest, codex=agent.codex, - options=DockerSandboxClientOptions(image="python:3.14-slim"), + options=ModalSandboxClientOptions( + app_name=DEFAULT_MODAL_APP_NAME, + workspace_persistence=DEFAULT_MODAL_WORKSPACE_PERSISTENCE, + ), + ) + return client, session + + +async def main( + model: str, + question: str, + backend: Backend, +) -> None: + manifest = _build_manifest(backend) + agent = _build_agent(model=model, manifest=manifest, backend=backend) + client, session = await _create_session( + backend=backend, + manifest=manifest, + agent=agent, ) - await session.start() - print(await session.ls(".codex/codex")) + await session.start() + print(await session.ls(".codex_bin/codex")) try: # `async with session` keeps the example on the public session lifecycle API. @@ -108,7 +170,10 @@ async def main(model: str, question: str) -> None: result = Runner.run_streamed( agent, question, - run_config=RunConfig(sandbox=SandboxRunConfig(session=session)), + run_config=RunConfig( + sandbox=SandboxRunConfig(session=session), + workflow_name=f"{backend.title()} sandbox example", + ), ) saw_text_delta = False saw_any_text = False @@ -140,13 +205,24 @@ async def main(model: str, question: str) -> None: if not saw_any_text: print(result.final_output) finally: - # The client still owns deleting the underlying Docker container. - await docker_client.delete(session) + await client.delete(session) if __name__ == "__main__": parser = argparse.ArgumentParser() parser.add_argument("--model", default="gpt-5.4", help="Model name to use.") parser.add_argument("--question", default=DEFAULT_QUESTION, help="Prompt to send to the agent.") + parser.add_argument( + "--backend", + default=DEFAULT_BACKEND, + choices=["docker", "modal"], + help="Sandbox backend to use for this example.", + ) args = parser.parse_args() - asyncio.run(main(args.model, args.question)) + asyncio.run( + main( + args.model, + args.question, + cast(Backend, args.backend), + ) + ) diff --git a/examples/sandbox/data/f1040.pdf b/examples/sandbox/data/f1040.pdf new file mode 100644 index 0000000000000000000000000000000000000000..77556e80ec52d22f92b97dc6882be91976e47eec GIT binary patch literal 220237 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 O0aHD290{@;jtc<(HlN=B literal 0 HcmV?d00001 diff --git a/examples/sandbox/data/sample_w2.pdf b/examples/sandbox/data/sample_w2.pdf new file mode 100644 index 0000000000000000000000000000000000000000..ecc05d994b94c49ac0533c1b31991013a28df2a8 GIT binary patch literal 1466312 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 z zCj)Sqzt?ga_X`>3aVD|QVlPM=QI3vq5dz22p(KtdVS(*qvy+^2LYH85pAXZW?ko9k z!PP4Q{gjZueoEM1KjjOSEq6DEHn>xA#8 z->XaH$cXYlFvdd3QsGSObO5!49^x+R$-RycY zI=Z75Clma*--jxru>7?M(_ZZN6Mt79`3TC-24mzxWid|8hOVp|#d%%p$Sim*R{ACc z>*A$0RvU6Rv#M$sBxu1l{HnfJGkBPtsYN>xi^}98(v5om!HcgII-u&`tu9GnZ*8|K$7n3kYEGn)kF^bwsIf&_#oIE*(FP~?Y=|@2 zv?S#q9p#D~E0HsfJ-(c&gYJn5D4V7+BgSdC;NcBJ>+Gn+Uo>-PF7>8<8K(`8dGCt{&WZsm+qN*k*z_*!Vk!Ti_557w|>h!-XwW&v)}c&9`7Sk z3}N8NUX#($Kei0-RM5|SI$LGu4|8UO+0*-%#Qag+Et5t0O^>WYt=X9pujn*r;|6jV z)&{S!9!1Ec*Qu+UOKbY5cdVszW(tGGlvS;L7U~*!*|M*q`DDGjX*>=KYO2P(fA1_- z%H>qP)xt1(VP}O2iROV(6N&`HSrWDM-ws|ArGs1_*gyvgzm7ZfzdSsELH(snmzo`rvGte8UHscOV9N03k&2;HvZTqpmv|5|Ho%C z{jZ+M!pOk-pSB4T`~PK|C?x)uZE|Ewx^`rnXaFOo(ukkiXn~GiVC67N%onUjHoKU! zjR?Z>895CcmKcBN8(ju$6#6osI!SaRAsQ=KcTcbglE8OJF-FHU2o_WDLPwCZUThA5 zRfNmt4XAEO>E-sd*0t8TkB^rQ6ogOJT`<4-%RQZh5{&O&1wKis=xl8>PxuROyw50j z4;X&Z-I{LPpN}&XN7a$V^ghbjM>$EPc{vD^ust>XRMu)ZluH`K&8rPDX)!8OzX^5# z{-JcLJ>Z#NaF^1c%<^%1@rjz?w}NZmOc+|%*0r;Z74H37Mc91f)B2?aD4UA^jsPWE z15LBtQ8Ad#JYL6#-byL4r3PU8(|Q%y&yFu3$(^yIq)Ytp*~T zfZQ_R-|vz4HT%r#WIPOUI6ew)#SEwe#%Dx4+cQ<4f0}%6 z??3gRU9|0%w(M6sK{ZD^LT#beyx&Q5rrHND5VO2y{gRe|+R(Rw#YAd49bZk9f8@|& z8oH-jGIEX6Zo*;AP6{_EBZ;fV6aQ@q^IFc@s$QAWwsda2Tosyw0&6x$cOnLiXHt6t z@+QjhVZ~z$etdr+3CB$m$4UmC-;8Y8Dt`F;5Fd#^DYSwRs$`!&e~dX2n7)+nU)v<1 zSd7fhl)U2E^c?q#buqf9#Sz zMKYnvPnKg$lIPnglqW6=3d3ZQxaYtP2(gRb8+;^$ap^udeBCjEk>RWVO+Fo*ikLb@ zgrgE2u!!HEJmD7W5CgDLK3uNfB3Mo;ZBIcLSL(+@{z^%rt4AP*d?0>j-uZqa{zKod z++`qsYOrrCFv#{3$u=|yQ2{mE{@GO)nMac5<-O~R2f))_Q&&?~GrQ{n^IpQ5bp$m< z>K!4Raqh9GhQm6e3OESEg|H(yOI|`1bY*)@{#TKSOm=HAr;gJPzOViu2|mm|5kA;H zQK0+YTVcod3jf_U!RC*`6Vc}W5pp;f175*KrWCBe)P`I~vp!u_nvauo3eWMp_w^X_ zCc=mH=Ii`?p&^qk(K1i)ClSW8hEPVaco!!5hn;?8gzsnE!KM)gtAI^!zkub;H z!~VrhFbwqd98KxF(ebHe=_P;DuxfxE2| zimkhqPP{}JEt#>msaIl|3fh+U^Gcl$vzY7BWVyV>ZiE=~kkf!p_kP?V;_aW!7 z_>}{~T3}r@mwses?+zI3LO%)EmR>T9B#H&~YTfcF?7biOt@ye7LHJpNJ#8xHbn^Tl zcrRYop5%Y-diedO)8a~-9?eIr8q;hXEQ`K&kFOV;(nea6LlGK!&EKyW8&%Qf_9Mbg zyFbVTi*RZORiQlF#ezu6z;v#>8N19gIT=x-q0#oazJLXgUwwDx=;d1Z(GBCZfP`kUtHmxER zGy*%O{qGkF`6@UgYJ=g!XiA2PVv`h!U=a3N_y zy;8e%$!G?aZA&;vz>l!$Gl0%mt(prvtw#0q&=GW{(2TV7cto$eP@t$`Z{6mh*so=P!(^MC|3ID`t-rq5Yp6-q&?^iBKwtL)bz zV-;ll%|8lJ?8GaR*q6RjU|!g#_dBMx+cW+~+M$Z8Fm=<9m=-YKF z+4u|BCE~!-{UA*{bn_+Hv#2kB-13yAIPF!|JH;oi9xzWFH}O&1=T!b6&;k=BJ4cCe z?WL>~V|{m6%XNf-aGo?L{79ku2$A=vUa9P4g*MRC>6%#qzAvVMpILdXP)B)=@X=ox z*AnhKAPCb^SOrg$`GnbCs>l&x)}UmdZ?_Sv@)8ndFh zz~YW&+(=Z9Q|#fhh!&?bxvsA&6QPj^#TVa?PpD>2Y7>a#v|IPI>oD_bgysb5giq80 zIKv}+=Y0kw$8y~`$OT}!MRi6r&V~AqLb)Hx-#I3xhIG)_(wuqddo< z=_1<@e7?s-_Ko`JIGS4BQd)ws(2${NBt1%is8d(jTE{obx$sH;cJ2$>lC>pPx9G=S zpOKwcZIoC(duy-XXx+%do>5H9sOEL*_VSSA4>GSauWPLfKSK)n1(QHQu@O;G+-H(x zv)3;pldzC9a~k(jL-r}ThRA>+aN(>YKxJ4pYq9e=uLFG)@C+*40y4UQ<%+d|w+8J!l$A z)&^2-D|WiNOWT1e2kNv{t18Ye{8OW~V2nly1+z(92$6{j7#=_S_iHOED%2@6F*6-c zV>bFdB$ki8{OUpvXvt{Qe$KP=EdQ#nv?B*_IAOoj`eq)Zp#y&8{3QGK|95&d&qh1Vqa^h%P*GNQ@=g*rN%|bOSo>=<2iJ>>1}2uZ z&oE;*tIrU^;7VIw;#&yQK7uE3I0W0Br)>&9EkIIZUxAkxc%S)pt@QQsuTyWkH@ed= zP)pHJs?h>+^a?)w4?iG(cn5bAKrS)Im3$*vYH2B1H*WerjJ;!UW=+(vJDF%=O{|G+ z^NwxXwr$%J+qP}nc5=ry&%Dq1alTXa{`l(p-@9t9-qpQ(_qx_-E?L`diDJK1c}3xu zeAp^U;1xtubwS07O`4D?@89vVWj~K{ih5CmxNS|3;)Rn)vkMI@(fL+>tOZEC1Rmnh z6ZKZj*o$Uf6p@6(3)-Vzo@O{3ECL~Gl2hkIJWE&vrI8JpA_deX4Oz>vVX+oj6&$Z- z{I8j&lKzol*FJx`SdFZI!gU0LuoNytG_G`yw+gqcOLXuGu&8_pY%#qc04jm<_5Gqe zM8AWzI*e09TRQDFSk7xk3+j#oU0l8g5#b%ow+@k72dkt^XmVcV63&bRahmmxYu)I?|i&=E&YIU~P>qUsn8Fy>1s*gACv zs6Mru<}HP|^>s%bW|#L^@@;Ye2@EYp>xThasy3^@r`S=Lx31tJ-OBM~nuSe9EEmsc zEZ0^I2r6Dc<@bT~&VY#Z$9GCJ09`^4YswqspCGrHnLdCUett_*1$DzwTb%XX`n^Xj zhpu7i^7OPPA@jnO2{01l4*|8_urB6dxwGaBq><};BQ?8#57k>HSSJ|Z20@r9;0;$9 z+3z1ySx!2ZzO=GJi*b_b{Y?BJy-=9asmLWDO$?jsSw7_=hAJZlP7i1mo;3B*<7v3M zAe!fCjeM!Yf9lmtjX{RrC8PgkqsM3>KMLk0b^V+pK*kOCId=NEirx(Yy2ne-$8W7w zi^Z1ed@PKCAlrv_|A#fb+t&=K_bq?0T7atr*c{^Q>%Pz(C1k+jws1N5#Ze7ZRGy8e7$JlYi+fnh zq~aCNdH~rC?OG+zucu&chASvCI54 zl%|p4GUPSbspn0?-`?d$w@*%Ov>_zx7~qa~+^6rB_R%zrVvxOBJrya<%$82q|ARe@ z=LREtvA&6nGbJNq%6m5PVk|#w@(<6^4S8me>h6r)UzSw3#xh{su_8ML2`9yV6?Gbo z;lzvjJFdFK@^Zi9tPqm}SWG|6?~ENmE+;+TPLvV@j_SUOO0xTUCNEIiV<1>W*6(Y5 zJMkk#KH# z8XXj1)3sSF*JjM>RLfV~>s*hJyX~HEGAlZWlWo%q@r) zBAnHr^2=}DKx{jhDRDaVsPP9w?a^oU z!M$j6MnDe*ljshp$m=a%XnjI*j?iC1KNzRh|1bzGMhxwX8g!VU(X2aFg-p||+g}DP z=Wi|AoJq;fDL&Sm!#v`?y~A%YtTQa!@{DkdGzB3R@7vXD^;<(q7W1$NT^f7}gX$DV z^pWSXxnJeGrwVrLV@Vw{q^IaCJJwtOkVN~u&3sx~7>)oJj7!E<{T`i?*_hK@<-4DMtL6h67d+)?$BMOi5_JT8ft?bD z1?|;0ZMJEcllveBy)M6IxGw^DXf=N|GtfUyVkp1_s+vSm4P*Zl1n-NcD#cP)hsHgC z5EJlcSHMyhx5$5h<6pu^S=1)Z9XtyVGhf7qVzyo@6!Rx+>#NymYcRTCjbHbl0d6`@ zcbU2ax8HTRywOXxkv8hzFHJAJpKRKL+6wxKM20F>kr*b)OJH7}xj)q(4DhSp@3?zT z*p_7moD?UKUJWpx25`GH0hI_RrVp-%;hVwpI-2u2Pl~+QxCM-s1d2rQR}-q z!OrD!z^YmwT-h7UTX^D2_9mE9w7YCli6q;;9CRw&fijk;)Gq;F!6s|W6i8rzF0(<^ zdt=)A-X6m`alYG6K!HMLrOzYJqNl%|VsI**Pn5g<#jH7Vip1l&0Ktq}lXm`v+{|7~ z9f2=uiz;mun(Utlsl!{p`auy529-z*S{UVqL44!iWJ&Pves7m`+D=e4!BdAz?O8t7 zHBXsObhg5RU9OkuOGg#s5|S#_ZceZDMUrT~O{hgb*Fx-xy@t&B z=(@m$JRPDl%ov=AwlWqi=AtbtTXT2~3&}8VDY^jN9v07UU7#25;69UAAL!}277KD} z!@#aSu|scdh-JX(I`K;0Bs&Ugn_(d)0Yd?}J=7f895wJs=$RTzXCyb0?;e4CQ8 z9vY@iIFpxKaj;b+M)kL6yWzOuF;*Juho1 z%QLe(I(jfQ$z=LoA!&s_3y-+8g~zH8Sq{Obe3Ee+w8A&jR)YydbBemba!M)J{#!X` zLwPZU85rT7kafk`Nc8?&LK1DCI+Hg)i>kPtL^WlT+4yl1144izH4XkZZhhGX4XQK+ zcz%Yv)M)$~EH*s!t)#FBx2B&(<=qs> zV;ChBgCM`dmC{9wiE+%s*LbRm_gIMn$Faw;<9Yn;?)--@8oJt4j`9!_R%yp)E1{Kz zr0r`-&rl)H(*pu+yn1me5wcZ+_F+_m@?mme;P<_UsEVK^)ki@CA(cpGfhDLZjWf6p z$d6)Gq~H#lXeh+S)rC@Wno{8toA#pM^YQ4m>DCN1)wiToETdZX>av?2`u*$R(HShJ zP^O&X#vdc0AvL_I+wcyt!4a=ZV0N54-32}eXUmg!p4C<9wheSW-)?6T-_)Pj)zX#SCbQVQA~!h!A&Qd#$R zCH{fanN!q^#S6&})(k{v5&M+n#OH*(NF{k-g`iSD008nz^{*8B=NJ0}LG#}vAlv*( z-m{-Czip$nDLw}Q(G9|%9Pn>Z4wKoRFW2+!7hJnAbx#Mg_&=!~(H`E=@a$^v<`UIa&^rVdLE=5p; z8~O8@-;$8@jJv!w@_R=GXF>ciV)B#ySu?>wyLH74aF;DMD{bE9f$TXL_!>K^BT7eV zUXMoc#>E-Vf{iOWThdah@$OfC%c@&-4ayVo0YMqILqVkd+%x`-Pkwz~ zVut;a@rJq~!Yk@7%WfM^;ul{SmjmJ9C!iY7ezi_vFb&~w(C|oGt9h@Gc2)FuoxWGh2U2vU7bA&+H*tNL-u? z_af^tEWfz#<6tTnK51Xf`Lfe)2u?RW#l2~Ij@tM8Y_%{7obHdxSrX4P#=6I zbxO9$c+rtw%Xj%TlT0@x&ZB)D@vM{2fH+(Xe4Q7nRKDZwRCFRN`CM=O=D|{Vwi}_Ww zlz;Hnbm%^3K-CLDgz&Sf|Fj>Cix=h_t!?M4<3J1%B1id2padO3j$F|jmWR=-6vsl1 zLR>#;z^4_b$7mse2%V2K4yH(v9L7Kt{)=2c`H8EgBQyms1VrHLrM#Qd`?_PI4VdjY z)3!bWFgTkA`!?5h$gJ$YzRLu^D>$^ltne&4uWf$~b-L!b=O`;N>LPo&F;_p((w{1- zy!DH>j0TwR@O+_a=d5U^lUgsASo6ITuSlNH;!#D@NJK}gVBimrx~$>ONNGHx-+geq4fH5 zM>6Zuhr=3@A>?FpF_!r?e7NlAcdUm_r^471bZQQ3dEnT0>dF`P8*QS&Rf10$qkEq< zjrL)P&sPi|sTlodK@#;K`oyp$>O>wqWY%6Q`I6aB{( z4#Pn5nud!*QGvHi$ZCr8M_SHg5?--3%Kl3SV?RZ{Z`r>VD3mDHIoDQd{(yP9Hq~Jg ze9{@L8Fm5{*EgflU#>_y?wQW(X&kigy7HN1Bs&k111DH{oVHR6?%)+~cwf11cUjdn zXBt(er3Xz9Oy`UCr>5&4%5VB5ExF!*Dnv9Sjkv68W#U3p#xn;ceDOu_a1JI;jZJ0p zF+}_hCgYKfObSYuGAwG2u^#zp<#d9u6 ztInrRJTF@rCL;k4EF$mzRt}(@e@Qhf4!xY0Uq0*~I#5@4L}rIpeFHZQy-u|4X=Z$B z>U4+6WZqO;XS7MkC1dJLp+5V)^Sf6UN1hOLPpesDS4Jjoy{jrFTNUTSKmPPjr2HIm z&v-(lLqUN4t?EtPV$$ZZy-uD~y=q>o`0wM_GI=W`p+s$gqUQL!!68LKn>I-%qFK4t zS`%FJTCs9rUZhU+c87eSyC%V8rOraNL+L&J%fy!u5POZ_jm=pjA^48#a4awuKJmbW z4QRP-KcQ_?$yi-$ZE0rs&e^@d2>`u$mvzGX;3jyXWd7~Y4oI}UAXN4CcU{(3Csa_U zR`|k>kAT_~+*Cy6D#M;~g*k-8y)$!APjs~W!jMR`#bOV}%8_X^j z<(bN2@fEISy6#+=1wvs)=om)n$Zv{uXjW;tgQW-AU61`(?~LL7-oG;E^Xd|^d95@M zs2qLy@l9V>0pfZ^75@1}a)>le6K0GlFBN&UjOA*D%W&zEV#_MBEg*%9$4DZg1fU@P z^3@qN{p&{Et$r4<)q*o&89sMv|8|gEz>Xh3r+SB4^P>yK;<)IRJGa_F+x==P`M_ON z!@b2J>sy*5J4NV-o#m_M)RUh#alkz?EUP0U*l~R)?YIXsYeg*dg`=9+R)6iY8a9YO zUdrlk%qCHm9>a~w;|5{4|CcLscM&Bqph@wL0>$?e{ogqlv_VeJ-9Ai3K3A~38E`2s`ovxKmg0%NS>BA-0>dzS8@H*P1 zWSC`Aci`0QGu{zI61E*Wo2#qjgi%@LktZ)BO4`^bFL-xf77wR;Z;jJ)Dl5XBZl0q+ zi2aPL#5&^?$HDckj4yc_O){IHPi`Fw_Z#oO7uAbRl=X%;?r&5>;@6BB>`i@mAi7cG zhOCA{i`W6ZRN>{os!OJ`goeXR%YM&=oQn&-A>RJqjj_Shw?!8FAm^-j9l1lNl#V>I zZ*M%j+8X*NkpBXtux^{IDj zmZKu9n*7#?%ZhwkhZbuc7|e=ulM9zT>ZoO$8K|g#E2FCC8}hB{1tTcK)XO9`a@#g#fsgi%yf#CX52-psw#7it*GuT*HJ3DPV`>jc(){DsaYnCnw8Tg zbZ390JE~vBw5ETpd>C+UWl9U~S;k9XnH4__$O!IU+ias2%&ZzVy)yd56j>?CZO({^ zbwO|sc#mR26*sJ4yy%t-$zVxPT-&(Syf93O)6I&9%x^AxFPzo%>*)@=o;2R>n;+MP zLnqm|6!B?|b4JL&)}m;hmh4V&8wOZ&col>hL>_V(9dGg^ymWAz;U}N2TC#Y-BsXNk zL^Ly5yN(9Mb)Z^m=EHsWfXzLZ1llUPk{=3;9y6N(a{?UBaU5IpyYrs>V7vXUfg7D9gaXJRRe2_QmI(5WJ?Q3hCJOGp@nB?g8uB zf@b@T;k@ok=Lc-$&^H3MJW*>G8}!gOmz6m~HJr21p~qbx!erI1e(Xn}{$@__X7oj1 zCInzF!?gxLL5&Z)!Q+AY`C26a(GRwc82jnl&xzrVnl&*4{U&t(qWJp7b|=}Vwcc`8 zq-P_@Zn|_Vx1vj^D??RrHL2~)nL0BWji>w`z&)%hn4v3wg*)9rGTTv3ahI{@gJhesDX`LFHnvk$k~uE%=fa^k2k z){#VGI zJ#DT^XdMS_vVkoZxZ4FuWQnfa6oy)|CdCp)bv zMI$-~76CNYi5&AP!29+wGpl8ybguEHRzvKd_v-Ej;@o{l`o)u|?opA^V^TEBt3uY- zz~2S3)x)wE%cJ6>C7Z>W70{; zyFLON%g5QjE=azs79U_rj{pATUPPsHBp-iWNiV+@w}NbF;QP5)6JCQe%1Ee3FHQ@ zICjTU>&re+FS3-yjGt$utf83sd*Rz~TRdJ(M(wOeZP(u8@zSCMRvc#PV4`2dcbcPJ;Q@N+QjU8SH08iOvUP@R}8Q*&G5uJNlBia z_##}`J>us@$KpWh)o?ZBoH?3gV^b7qsd|=QFM(nR&{IBD)*T+-0AL8Pw6I|ys48j_{;c*bAH_OX>|(}ZSM`F zixx$1?aAZEXCy0~8uSL|VY5sL>rOs{rQ9-S`-_vnf3}CfSH(Q-AQ?*+PL)Sb)cbBf zCbniKryUCU4D;yq`eY2KFOjPg_^|OZRa#_#g~ov>g2ogo7g2lS_tx0w{7B>Rn^BdM zsunHuvA7!K)D15;h~+2p!dc4f%vLdKRd(GbuIt8!k$|X>^uvY_py^>mJce!e8sh6Y zck%+3xnodkpsma$%Gv|nWt{YlpXTs*b_VCGA=c+feWSSWK6~1blP=0SMZ0DZ*yy`* z_1zKE$kD?T(@9$_{aQb~m3F=7aSwelYdSsel9Hk)ouROMFL3@WGyLmz5R7ML(b~C@ zq2{_5z%0V?1tvSt(ca?qRshS(RN~ZlN;3b1e6cB_szDazemexnhw>qh#-X$ zN8>aWbG8q*{<*S8$LRqw#iM5%Fy3nef&*zo=SS6*riSTYALnf}V|k;zh3(4Sa^+J>K*OHy;ig@@ zk_eBHiyA3^AX-x@&nmdW=wMR-^qWv2VTQ1o7Wd4j ze7D2~eq{ElzBYm5r|lotKStvff9X1Ks1LlR2%BM#-Ea1cm+u)z2rRKx$H66LrFe=& zrXN=F2P|J^G>0bU>>Wb(wFTSi0`KY=|4fhlc;9K?H@>Dht$0C6J%9>LIrVK#z;}(W z?@@ZAIQ5yhJ|&9o8RD+M(l{{N)XbSebDIXL>(gO(TN~0n7Hv;$k7TZ0ZX&!F$s!Mg zhtgbhK<+7aeA`H_3+ChqObW^>0e8AMy)l$dU zune8~%6puT+w$g6_68@#3X@nDOyns^37ukeTXn6L6uN?`U~>QU*G46O^uu8{W>hhU zpDvHm1n|BZb6)oP!mM;k)oN3p#`DOzd)wVZ_Kfq?V~chKMbQH{8U4kfsoHv#7}dj- zL3UER?nA~gG;~rjZIf>5$fLDuDW{Ib4q+Nj zbxh55rzY>xW#lvB?Vo(VD7~TVXbXE0`{HB$r=>nqrw=(~wyXPQp%-tXL~Hy;icptZ zk8=Jd@iv>J(eq%qf@Zzs8R~~-<;_1(wrRLpL=9c%wHkP#A{O!Pjq(VJP2D~X z?|y60yh+1)JUdT%z}_Fn&t7E+9pXga)(>nGe!D2!2uZ`GfSs5ShQ*xNs^PCtTei5F zWch2JHW(UZtZ1@rYEhU^4Tr`Rvh^?%P2nwn2O0MCTe@9<=iy;3RlBD5$*)KAnESE4 zXeQ6wQ`!s5*~~YEut%cEH~Cf86ZXs2+oD>!q3MCwYTA1c(}imjH^`^m38Acln@2$) zMdTxcF#O}nw06X_K3dicb+DnsiI!=dp|t+hJ-v-KB-i(8d(Mxy%t}vuu3^fu6^6As zE!(FOOT0|76ty1Az-n(f8N5=TIa#*`t^9Lb9qBBO;iGjY&l6fMl`7R5>kO0g44<9; z@1;=mm+90d-Op-AOoZQn?o`hEQb$l{iz4cm$=7!f*2iz@CpK&0$T=HlGby%}CxCAp z^0_Y?+jZG>`_~zlb;v~KHN*8c`NC)oR)K0+4;tJn*ESCvorLm3v33dV7qgd_*mQun z9K>fHcIM$WLa@JG2$iPg%oR|0<^^b65e=$-j4o*Ccc5h&YP78$AUSopRv(SI!tkYL z@?LV*u%=mw8!hR+H4CL<+dvwv9_(F9_H#5I)Ol*8p1KCv_OY||K2sg01Aec@2=Tp0 zJUn^S{Lr)|gvOylm4Yf4WLN;)3qQoI+?Z-i7Dc2t+zuKx0A;t-kTykrMCA-uPsKIM z;*$9f6@5==(Gas;-m)Z%o_<#XiwTj&E#zv}>YBdqE4Zt$xsE3~MFCp@_1w%M;fsky zz2Xa+4C-B87-Bp_kD75@p0#l94ROo=yjx zX%X#BT`JOd&VFI1r>fX3fMcoSMgDZ!T?fY}__JsGYK%&7b+Ia;|XXlQ` z93TsZz>ZwxVB1(qA|en+UUr~FAVUf<^=D}v)A*v15&@{r=fu`n0gtl<`##jUUtsbiK{Gf`HuWKeW3VF>Kr4 z{_W7s=&2z1yu<)TT^#>-_e_t_VJM+-qWkiA_w=afl^a@bb`?Cz`wB`U? zqLToF-K%_C0?WX8o7XB-!$*ad?!WC#V~z99VAgkKV0As}PaT3Z>lhDRp?B7UHV9QI z?VM#go;j3__Kl)G=+60{SSOn}7Bvug_q12*%$=y{{?7_<2Ho;}uU!knzk%Ep1weIw zN1>@)vIoM8vW-=M^K8TKwlnSBYo&s0fvix@o2S1!LfEW{oDb$NK5)i!@Xlv0ojt)` zS**F6<0hOuXWAvQQ~p`EStW70F?E4wx7~=|gKkwGyf2DL-@+`NxE{io?@FJ^7L9_V zsFDoXqnN|^uLvx>G3v}uR`_`1se->U!HJ&z6{I!SHXZ`_lu*FInC)4= zQtsn%3r79|4yNIDuxFqBM?vDTpUPCTJ{jJ#Y&Wx}d90Z~@~o$n*TsVzWn)Hro4CWJ?Kv7z|dj!XSh`OKclGCfsE_J`P5%z#?=Cg=uM=-S_9CI`9ZFFKgxIW%jx4S6z zg+XD-(3nHVPm99Irv0Wg?!Y6;yA|Cc52Gw{T}&dcQ$D!H32nXK2p~9ixamTJm73E3%k9Jif&*BwpW%V!!3%eQHigWQ z@dV?sq#TqA+hV^&IITO}jp(B$rJj!9(oJ`T1%x=R0*wsv-90z6Ch|f+b47i!jDADA za1gvI7#G8IyeVtRw9Tc%@-FBL`U>#Ims1@RS(c=4L@d|F?xvHi) zU?3s@HT@#kPp}Re&&YW=uyml3hauHl9&Y|de+i80pV&6pHCg1 zO*{mCm>P$23j%_8u zM0&%xr!@X``(~DlvBaG=M$#2618>ugq=I-l=bKa^62UG_@|7XY31DsGLsh<0EoCiF zDsP-J+SPVe9=e47b^;x-TUF^z@h(Ip(K1RY3v!7UiP8g<53ea^-yk?3!3+})%Y^x| zMl1__P}vjGs*gj3uTY^gMP7IztgI)I^N6;%=ak~M|{v*^0aR(mx0P+=N!qP2p?t^6=jDA}jPnZC7UAL}lp4HQO zhMWoU;ckna#MMxl?DZ!W7{;PYt|lY{GhNGFu>?x;#@uZ&`lTlQDVkIvxS-2{YW&fU zKkF^41qs1#IqKP9Yu6vprtZw#*fgpN#vKQfPkPif{frfQXrPWtdPg2* zrP~4ddOyE;XL60aJds2lkXxfn#eK5y`8X|ZK3!}=7}$GV$91@Z6kq{Vzcm^zTHPG! zPmUXI9*KA0A+1sZTQ=H;NWmRn-6(%xSam|plVUzh#ij2xnYfi>W?KvwrXzU|w(L!? zHjV+Y&ERxJFZs}okW|FcVAtt_>kg)Tvd2H^qg;O!E6HV;bQ}y#eyHq|uT9#H3}+OX zx`Xa_f^dR*Rb}n7sUqBjJ5VHA`?jCd4hytqA5jpju!{x}&BVW5;Bc zL-V(a>-QOx+u3}!xm7#dDs6EOa=U^V;ZFmW(Dt{ei5M$+zP2TzCbw(7+n3) z!3Ta~7oCOHpyzA!sRaFnain1fXhr-;W_jZ|VCJ$8q<{8S(W+OY?$DDSM}QHLQ4P@&dzy;hljnhke0WMZhz%JmtB`U~e)B*!guY>>?7x&NbvCIHb1` zfv(7Iei|U7mY#cn7A%4KI*jR(`A7iHu*yKi2G`W(a-}xF^ursPYjRm5O~imc9aj#G zh>KZ`&6_$Ed5zz)U2fhcYmD_xoy!v8r3BL9r@KF%PMl(#>g+&+WBq-7iRrTuWS12e zd>n1a=Y+-$S5`e{KIE?-msS}?GJsdx`3bV-=mY2jCaUy~1e$OKFy-j-X^F`Mw3bE+ zerUs0`V`o-+&%urBe028{uOvI;u6F^n0c>6V-T>5qguqFg#L%{Fy#LiB$9ER z;NN@xH+*h?xCyxt@qQr$i3X+DK=u>;XNmZc&nE=(AO8Q@B7P#l|AGtx?I-95+4HmG zV;5rogM$c<1cmq?gvfvXLx`B~A7TFq7Z6F2NPqp0iGlb(4@3Ab|NlTj`uQJ(ghDeV z&=mP_{~;}e{T~@&tp8|k*19$?>iIeUKM)pz|A(*`%s(nT&rvmC;WkXbHG-G_3|;wc zfpmbngs$SQ>nMxWAqRmG@QV==|AG<-h!YY+L=Ga(hxiW>A-MmD4#TqEq$~`}>89|# zE10WxTJN#0B;fryA&G4lj zRpQyS;_kQYL@H^K%I=zl5`S-iAR&?uyftZWcs(@jkL{#R_v$cQh=KF{xs>O80+AGC zlE-euAFyr^=9*P@6}KVxJ3UD1Z$Na+KKAiV+@6*Br&rglGT>afHeTDF&YhyyHT&2kq47i*#BC zx@+-Kc(dg$vfB+tAKi-quHn;6NO&Y}RN$J~bN}W+G-r#RH?Bq|E%x+ zPvpWw4F9+%R_g2m%@steqm~!h*PrYk-)w0Aybst7n0`F<=^r#DvQ{JC0lSSi3*MA? zIS7x{l1Dj_&gaQ5OvC%j#V?8MBv*qO5b3st+b!h5bT+Bdm;UzwYA8_h;Q%z_+mKdD z+6Y(b#`pb*Rh0!J;VTOSR$&m(FDzI6*L~*-aYZlb4;6Mo%IVY%3-2|mEc?V;>!zPp z&556?YCEY*N}eB6Mu`X<44r%x^?%`-rxCB>+WUfv>ndV*T3f|n|4?@Iu2Q1yDi!|XhU^{$vWIU zy&~xd^T4*qavr&HG^D50{@M&k#1O@kAHkTICgt>i# zATh1t#$o5@rbrQy508rfteUNF*=ZBY(uoGt!%yTUGX^t5dLw>Q%S1>K&>5S`@|$oO z%ys5iV!wh0!GrC*Y6|?pN{sFv8W1b68GSJrW!pSn8ZhR@VsW< zK`tA_RFAKqee1+s)qdk!+zv6zy?TkI$-%|(1>UoB37zLoo~aKhIiVz{Gp##1F*ekq z-K1RT_;_NfqJr)mTetbH{;_`z4F#AHt-p&`Ju0oxs7h9rZ<>fYGPYu%)9v<4JK
r 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

KR>`viIC12TPzhy!lZQ`4UHLkE_@rtba=>55 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

c;*4SSZ=fD%4M9Du%)1X&6YF)VW13XED2C@Bc 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

x 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

XwX32(T6G(ye)BEJ>x;W-rp9-D zb(|E4d*-WK!zLz2(TH58h(RsnS0Gn9uhl_IvZIvs$0oyEo5$}!s>cR>S zzy-F;Lw3dL*6E9}jyL|5+BNQ!M3b!W=2K*7z?U;$qZ6t8Ri7W`-K~|*G-RqGm(3oE zx$_3qWItAlC(0X5G{!d(a7va+y631TvLGfWlO6U?3zr?|cb+57ZRSK8Z=R3tKDZSKiba-UYz}@_EI%&v1+cg(D8*# zZjadJ?{W+ASv}saF2*vn?zmd>&gjf&|4=W-i=ZRc2P@7yEM5r=a9sId9)GgkvYhJ1 z%<{i9rBzHlA>^IDHC0#ErT=k}F3RQ6+GE1D&VTP-p`LIMI{=7?K1oZ(!b_Q%R}p$~ zxhTaTAXEL#mB*}ZfhVzEd0+9dRqwij$}7AoUFCrRNw1~~guOu98xUcjcK8DUUykqZ z$+DEb{`b8*VdM-Ow9X@Up!vztEpAm%PzyLmEHuC#79$@Dbb z6Du4>P9*4LEBnksK3DG2`ZR84r}SE$!a;1v)^Art(dUhW{yLW$u|x> z^vYZfMt>`xIn8jr^Y-Z_E~nJ#==45Ts&Wg7VR<=i-z6s7c=c#|L{^Eg933j;e^|O2 zue)_SWg5B{aw}Nou<$&c>g==%y(~5Ang`f8-K{_j$Ao(dsgf(6Y%FJ@b`bS9wW_^A z0xF$eP2Ml~u;o*-mms~X?R08#A}r;`KJ=uGifeOe%!m7uviu!>PJ!CW4X*?QRS|u6 z!%Hjddu|r(3YqKC{2|1_WR}OP-G@65Dr28r`<1Du-4V};!%LyZeEYcsidCtgs%jjq zO3w)=71_t-l&0S`^>fGyPcI)?%r=@l4j1!7mi2v)23E2OK87|{tlVxjvk7S=%Q2B0 z%P_6|;s=xcSOqq>cAE2ST7y{Mi$;Gx*Niw*9^>l>rNv43&9LcIH1uL;G-blaVznR8 z@M)vQE#20}a<5lPT#v9%L-!h#0v@z6Ml9q524h~&U-|c63U#|8XVoGLK;KcL&NrLcJ zL$HxJI9>l`ZX_vM$7l0q+oojwIruf_yz1nn#Jb&ZsPzv2Z;_(-Sjd}F7LWOv#}vXrLr!vt7U+E z_T#$!yBu!evR_jKfSGbCge3jdR*P^0@{)DmC#eo*Zo%c7^R!(lI3rGAzSJ(e49fe1-JJ; zrUn)EyT0~8?0s1;*^!}$rm#ravpK2~iQ5BFmvo*gEHbk1xi*2$g}9k2Y# zsONAHKsVhfzjD(t02`Pt_@I79?mWhvYKm4S0z4btpf%oY^Hpw82(^e6a&5PL>GOJl zt8WK>y__enjA-q;o=L$A?OKd1yltU4X0gwh&UtF<@YPkT5q+#e4>(icZuGDli}*$5 z{Wkf^XSihWca!Df%4mrpRz9Z1owGH1p>tKqAD&j;m)1zB+>eW26^zxgauUPiX*31+ex*+b} zdx~z^RL`aOa)GvZ=bQd%FgW_7GD~NOw7q=&!oxGf7mMzF*GhfgMJ@%8p$a10bz>~wZj_DY+6*PLD z*9UEuhw_*1(HqD3YB!$?SFhYeV4_#cVDJ8gDhr6n_mI@$kR#V<%KmD7j%T;HHR-N5 zc7XBC3dBGgVD;ASreRu6PT5;-D)_th=_rksg>(H5T+w`;*~d?=gEM~-`;=jBuV1jb zag%u~`Xpl}PhyUM0G^)7Fet-6=pOH$+tbxUnB)qD!i)@z=la$Jlb`#8BjWux_6 z;(E0!!jWZXV0v3h#ewyULH)hP+rG_qWJX9zw(EuAd}Jx&=x;CgvS6E>BWgBZ?D2v$DBi}q*b!+Fy?mAmOicyb_%jNE9 zs*U{3^C*rFyD3q~;d!iqLr0H({ij0dp@ol5&wj=8Q=B?Ws9^fDXB)i8pBLZrx0RN> zQ_8?Ba(039C9h*t$(SnEAcRep9|Mat)>r2Q1OYuf5PX!*(gOza@3qDYTN%s@VPn8% zZE#zPLRp+N~uK=5`{HGC5h~u1fN!nz=>DbEU!BZ0@;+uQRKaM>YJs zF}V-ebo&6^dS7(;MVB}h{M~-5wXl#ssOdua0dVU1b2|`5C6B*r)z>g`iaW}NbLSLS#b60XjkR>V z%otdNs@YC^V!U2W&a7YN?7wGB9XjsGe=tGwH@m~3)GeG=squvGww!`1yL1DP;1%$_ z^0pk<&#W>ucL6lsolW_L7Nc{!TtDk0?me2hw!Gx-1~&8>=<|UwC-<<|n}@7A`=scN zb*;%Pw*_72z#Q&H)B*2Fc#pA&X}Lq==M6^i)qe=Vh741+>)BUG%vvrI7oK)Nt=%Db zIDfe7T+ViZyAkHUHw^JHp{P4w8$iar<>}IQJt*Jqysd~;2fyHEG5oA~n*Lj#p!Lut z3{kFqc#QBUiv?bIWRPM_=8`V$QG&cI9H!D(K8op?S&)#BSZs!4Ldb796xRSDvZ-z( zUbOz$rw@zdoV7McA*08ukLo{D?wQhEtxyFSWibK zRWH*g$(eFi54#5Ai*3G0{N>lO>da)WTw|?^!cb@5QE+WD+x}Q>q3-NT@>wQrL(D*_ z$IwE^vMe5wj#_q6`xDpFrN`()X(ECP1-z*nZKG4z0Vmg?abX}=N}Swc16joVI;+RO%OdLue6UV_3UR9XT@Vb;x_$r7>7@rg zTpfN``YbYD-Wytg!Pa6g?Yxuu#uthjza)DoXMv@ zXvo^4f6S#l)4rNeU$G%8FDCtjPJu?0Cgyk%3l3)GHd%$?d|>C{X1Bp)1({U|?L#i@ zPe$f*yFALw3zj!u@25MPs^yi|ua-9$IE3Ztw7qz{dBK0G9jkpEB-ZJ{611b`S>s2K zZmWkq+Tt#6RBAjay1!6gDwb$ZTT>iY5~pTf_JoLdPAjV;EfgEV8D4&z)$oiMRAw^F znkY5Tj zpiiRBPH@v16s>eV&UJwd2c{)?c&RHF@4lFul&=251jz@kpb!Bm2)hgHmmWuRu3JQR zu>%J?7?Jq?Bve(edkDN{A6^#RA&LnQQPG;dm6>3o*7BWR>Ct0o%1sc!JwbVOLkmGh zBt3PCpF(o12J3A#vTiw=z7YuT{=VN^YK=`vmM-^ok5cdPmu!i#eHyrWgcQ+MRT`D@ zvkwe0DpH3bgeYAtd|}{A$gJKmYT4{`pSd6E>UjG{E`U#x|X_NWNN0moE z7PAk$g?EWY?Mx1b!HR|I`#84O7%IM&T|m_2^yMy$_zl;t8%81Dbk;8~)Xq}$GRX;r z-0=8RW(}GzxrQ>z%Q9SvA$ko9dF6!;hjrMVA5jB*IP&`+ErjsDPr3O{JTk`K*}Ct^ zb=bW85>C1w5J+!byPR0Bw=lW}_~Ua~(WW?5M}U&Bgx>(OH^JBbTvBB;$hyCcEc>6s zop(=a`m@!Y4SY8UzLVE@9VR^7ES8zL9O(J5ZoZx!4wVDg>)MI3#x3~g^`pBLs7PjZrp^E-Gb3Bn_al2_D^%`Jte^g&f`TlPKWzYJPArl(MgX&KW+wKgW-Rh% zb`}83Ph6~g!ovUl4oNy(H*umWgB2s#U|?)@0GA+kC>)0ZTBd;UIlssU3Pv$v4$5hm z(%xXJxhbC#9!e=fZoS}_y^-al?~lbQ`#>Pd)U>N;CMcD_D3@LF_09H`!rP?s zb;nMp{g?YLXA8qN-3$X4%`t^e!9M_>0 zyS?d({psH$xrC%2Txr7nD!+9zVmkCk__oJ?b!YH*pzwE~_jjQ7ztV@<4utEK`q*EH z6&Bb-SvB^V%;Gbd6jp5HFy^AfUf3z6Q@;zVX1~id{HLnGTwNkFpvd;G9OWPEbFO;m zj_8b$#>*DuIwc+*Xk5}<2&rpP+Buo#M4q>kD!Ktxr_Lk8tbYHbXQ*q=1;<35@ z*^9lM;vnp>q8qtnj) zZQ2OP-p+&8oV*G(OxrgUlZJt0nvJ|RH|C$19-@@W`8Y;l5CskHoq(FPBj9or6TezM z`3SX71Epd-9`x&vJr++kd^h3M7h99isB{7!Gee;)dx|0k+QnEqGOvSh1DO7r_Vb(L z+hV4*L=AnEJ(zZ~hewc^oLHX}{sn>)S2DiEgf_1iIF=R{_|f!D2I|M=@+=+;91ER$ zC%0fW_e%k8U6p>Kpn{I1{UP0mSaE7gp?1g;#8#c(QLEsW`nY9-ezd zI%fG+AG2Pu9o#qJeswF85!eS%3<~&q9u8d(K72x9CxLQdXEJ&Is2+$w?anyVJK|nU zeBDZNvH#RyN)={^QH?o95m->`fCTw6zH^2UuR`s=%NvZnfhY%K_%yfY0!6r^`p0g-P2NZHkK7 zEU;yF5z}wx&F`JeU!?R!H~9-t=8G;G1g_bhG}+*J>OL|7yKCsQA#AGK?p0ynJuouJ zQ7h~56ogGc7sQ+5(r<+=G6bWnK|Uqh4qh3_HyL?Tk)=pyVei~X1ZBNoMggHAFz%D| zkLCDKcqZ;xV8wpxM&qTFg9Ho1uNqoW8asEPH8+t)AvL_$^tK!}w4UDBWKvVLM7un- z&f(E^=r2K%R>0I$pwEz3TW3ae8in%`;X$Q_8(ff5n zK&G8oFtHg~0M!R0whF^-%`M2!G~DSM`^t44_|yJk!w}N|#D4M|tu&M&!{)$4EE}Ou zdVx}l<1R^uQ8j**p~T1*#smtP5J~N~ zx8*D;v%jh=K0SsSMm?fjKu{hY{MCA2RUf$zCPHOJ0|kQJwrq^hezP=mEA$5RC-urW zd|S|ae2>aNI#StWJ~xX*FlzKi)q z5f}aPDx4F*1!U8W{eU*tqZ!GIqQ~F~Gd^Mb1&^LI`&Zt7#;veMl|sgD1g!y&S2j%9 zX`aGOCg)#~@ycTy=^feLy|sLTU_6DwlnU+n5o|l=^F2Dxd?=;pSCEzNR=kanp{H-a z!lI_8-V;%7$`8m|HRJI#WzNk{@k7i~4ii-3-9xN?Onat#HI(*)MhRC`{wit(NA&|& zwSb0|f-8`)H26D%W}H+&~HO?{M-SSi1s!NuMpz6GWC7 z&MOL3)^xO=@8JjnTpR)g>CX?x=old=1!)-Hi2N~T7(Y-=gpBt;U<7}dAsH4+AG0Jg zrR@y#4x?o9(=7;&!V}fs(V+|RE}&NStH(i#zxSssFjJ1ICy|X)|N3)>_-M?M8YHR> zZzf%yOY##n|1AgRC-5lak;p0+>ljKBQ4Dv6zeJKK5FB1@h>;(d%Y99a;tx$w9qKO~ z%Fd5+!V2W=kK^wCR6rHy3r%IjIwMIC0Kh?!;3o>9ur&stph)rK5yiy>Vunu{3Fc!P zni{jn2T2wr7~;%~u<~CMlE5lRXBeT(;1NUukn&ZGNM=loU}n@)!$7=}v7s{IN;#-8 zqWq}o@mbwR085!yT=Q<60uG}uGg=OeGs{tv{^|wJ+q?y|V#G5ZQCV=aag(9gaaky{ z*yMdMlK8z*{Zcg_%qfN7%_;Q8kFZh$0rcY%ttbZ+LP>x39e(VHYeL_~R|O@+R~dsi zHT^&U&D2MzNk0kcsUQ_BFGDUyNl!dRQBQ702~RXeaZl#_!=BRcuI{HAl4BxGe*jZH ze)pGx)2;gc>}@e#(qm6amH>`8eH05CFdVTV*GzPjK8yub2hOBSJ92KIDDMR z*#1$Vv-PK+d>c=)sdr!6KA0hX13`nLBlEAomWK3YP&u}JpPmEiOdDYbVwHp^i*9T^ z>3gbQ^xyLu`fI#1zX|Efz>K&JbRMepkt5g|_@9Vtzud81d(;cs2z@Z@B%U$WLO@2% zq{r3UXa%v|ncVJENZcltf_5Q#@s~-}(#Um|Z4ywLFno-)C@0YuxZ;t_SNIvuuON)eVQoh0X zhJxfIy85;Zt|`w?F-)H3gzck1~6h+=|>YS!15_=4~ zb;+zc1ws52f&RxJ{M7#&@r+l5tOBDIH{Wk4Tr}JDB=Yyz;D}#u@%Vk=l9vAswP<$J z@IS=heH<=Dv;UAgpiXhuDB!tV{=Y;-uDEL)@I3b3{XdA5ZZWKt?t6yKGL#@XjI?AL zHk|+D9Lbd_f5U-dGAe?WI!bCHPGA6=7J^n%n=k_1$gj`WSWJ@)Am}DPdd$z5F<6gw zH|Wz{hQLJr@uH)wFWbW7 z_$Lvu6o~jDnBa#s5M?Cs0`e*c;?8~C@3qoM0LS*&>`8n#UNbs6`6?bE5+$j|^@RpE z0in){J)ISv?#tku777kb0_KMNE~SG1cFA>5*A*~kC0+2pfyFKm5edY61Xe;4lGIm{ zS5s^I;{S0LM%%$K4sPRXai@y&W|?t+YEKSKSBCPdQ!bzU z93=j0ZOJ+~c4ppRHbO;hK8QJXBuHi;;CnjEd|6p(T@AG(j1D=i=20Cb2$-0E4EeCW zN1pS)25r>$p#!-Uk|6dU4X4SYVLywc;^AQ4QbSkrQ?&|8Qr!XN3Cv1i7r?J5N8s2P z2-ebX^HKOCh^E?e8S!omnZNY4)C`0z@Stjqa^I2VeF>xmCq1ga>vRR9`>*6K7F?IKasudafwy* zGT;JIV0iLpMnUKxOKsLW9hy@<(sIgw)9U` zq955Z%<2dq)XEv8mOZpqYrENwTW5GDlTWugmv3pPK6as~0eP+Y(B3AK>{P5zUAxj>1dUc%J9w zl|%_tG_z@{yc#tLWV&XIg_8x(p97ZlQeoPr?36MGtQs^c5g3v%M{EZYjoCDr427+{ z4UcFbP;cv;|Jp`P?ti89ABIR2Erx%>;TM_JA9h<=^Q_sG z6(R>c&CGabfjH$`*=J^aj#FU_`VZwY2lkzsbtO}f30LE`#ZpNmQ(#H{`>c4ogMV8k z`PYXV^`ZX9oduMSxwzb=dj0hOD%uTmoT{$-=8^Lt08gkwdklF=P)vi_j$=dW0<=;C42@V* z`6D+kuU{s7zfcX9Y+gQS37K~xu95M|8r}(WI+Hipv;S-M5CQ4H4fwa)YWl}ET@7Gt z3x15Ly7|f{?;vhOkg=-NJbkpv%+uBIPkh8f9=Nq zpa}yF0?xgD{S;?@s|6!`Mb;>BkdmjKij7cnJAr@2#*c%z$d4FN{sPg82+GQo#?0Z}+g+PP6FQCt)h6zvtVmADJ?}9-b^UNRA z_Daw~Lw)ds=L+Y=O3^hB_x27CzeU`z5q_Cq8Kv*V*;75-E11MWDhw6VrVEO)W&TJ( za;!~93iFRC-U)*i@j?IFokz?6%U{WuEc&}8JO#tw!Q0XG)^nNmc{k0IAW8iZJO18! zPs=32KolVP|fa2iTaXHCZ-mq{%mlAe7zI@OfBhB7Yom{;{m_buNc z@jr&^0BS^E_#g8KrVIGTJf!IN+*+!hgbwu06kMELpq`CZ;x7kZ{rZv(l;2BOoU7O% zhNw~Xv5`hdslWTBFdw{H0-lha zImETHGArxyQN(%UKh2%4%JZM(Mdz2w%I35pB5xxjg$7h5&%`4!{k@eOg_A1s;neLR z6mIZe|0kG8J%atmMq+=^T{!7QdX0CiwEE8?AQ)j3=?&w^TfP>jGsTP!NSGFv1uZ`d%l=Vp2T++QTBHcmjZhj@6S)2)(A#b znEU;p0};_n>(i9;fePCWFS#g+J(J*M!b65;*Sb!2Kacog18syUap^1$7oy zaY1Eeh!GVq!iuUGupi1#d7T2RRS5MDXL7u`4kZ_P9+AlYeS0`ix&jvQm!9?^*ye@0 zxI~^V!Vu-Albkn2U|8ygE=|dKPdQ6TBApnS7-OOP7OcEdT+yCqZ-zSuIyK1PmwKP6 z?<1CsvLS(IH9u><#->qB_m+1?mKVYU_iaJm^12H5&_^C2y48aSO6dgU#Jo9;z^?R@ zvQFH6Gap&Gh)5*ln_DO6yk)KUaR<88crxkRmv*wIvE!Om&hi19AJGw_$>01BKj5H6 zqgW$1NUg2}TNdQzh80oGvYMbV&0+tG6oqSRmT~~QS>mSXQ<7O99MXuc z!)>uX-RLQ}$vw~enccLgwgjV*P%WyA!201Eo*h+|e~<+4)U@v^FKcS8`Z-woZQ9kK zAWJfI#j z-@R5BYq9f;AujS}9{Hbg$XT$l`+q_kDv3i<^p$k}>IC5C&KEcRYA;QT&*dyW?^kZa zA|y$xyXfaW!+^B95_D0vuV2QV%eJF~b8}s=GpM%jTkECA>Yuy0X)B1kj6+%QZb;jU zOB^!H7%jnazP*00A zOxV65_vEiw81jtUYJ}8R&OZP3_VjK`t&|Wu?SwzOAKsWkn@md{jsMeN?=MO34B#NR zxQuAR$EiO&ROyThHIy`t(oXES}DhO?caiv zudgOs{4F246;ywY!9Z{M^Kj)TORDX@ndYxhdD}gJd;?GPz9V*|rF$d{vuc#w?y~+~zeyYN z?6h* zQEjR|W(W!SdzIr(qRVL%;yH1Mr^R}!QT%j`i|uKV%V+g$jE_I#UD}#;nspYKNLCi+ z^GU~x|kGFb~f@ZTm+SHdS&`Fc@*60iMXLVn=7 zM9M8QYay8^Oyk7N`)K)j_-K_6mLa>50ih=S99I)TPU-Sf_YI1k2V9Yx!-yc0M=o8P zGRn~YF{zl7Qbl>apGS!4#);|in(9y3i@!%%>waY$*gzP4?=dtD&l}bO z8T2-fP?5^<=MQci$<2hZZE0%P2dS8Dt3%QzrWcW-YD+|L+-#mS`9mDqLzW-&7v?sU zj^+t{g8q6DG8xs!{AG(`HcU*S^>TbcX;N~03G&9O6Q}V1^w)tC<(DQ!IQ$569)^pT z#MLjfyuq?B0Ry=dJ?yVZp&^LKY$!YSYQEw%AAsAh&xW`?w*g`fn)&w5P)oKUnusl@ z{*Oe?heRB7qy=_f;F7IR!Jxo(m=0ZPa8*xsjSwVptNQu~LymIS+070etU4!3V~-9d zIAe`F@9BaU;Kg!=1Ae&p=u7U+`e^H5de!nO5ZG2T$F&+r+JgoyXFGYhbrGc%h>tL2 zvVRYkf~bU)MPl5hh-8AH5;%LEL;s`_Fi~Q3{Y8PoHECN^aAI;xc}**OQ*?OJkkMVV zrag>QTM~Vp@C;j!T9d>yg+X4QnMJEfm*@=TU~7PVSuF&CHVwD#N5ggO&EV=@w9bCC z#V|4A2=U7pF~fee&wg~_%^;SCiGMqr&PFP;0IF-Cc@2R)LVk~797=#chhNWfyt|0{ z*K?!dg1=v}=x{Z4cj*d?IB>a*w+uhm3p`3zIrbXPEP>zG5dYbg3tqBlH~_{rFjnxp$Oem3pj29K&dz@?u(apZ&dp<@VZ(iP!Ao*wyVZ-~>1W$`>BSb;I`+1NL#t{ti2zYXEVMtAbYDc(KTGqfn`$b> z=c&f;Tu=_2$qF9RW6LMAg|?2S9w#AznZ`+Um01DI`$@GEt0=6^@Q6xytqsAjT3s1F zu=X;msMXim0TK6sn5fEJ>;xw#?eN-6^zriNSmq;-`Gqf`9To-Wcz$bxvKg$azgN^a zODr+ggV*HTv`VNmR`!MRfxNanEldq^eKy4UyMCU`T7Ph$H*%d%+aP$wHJrH2{)6|g zAEVD*c-w6f_H!-Q=9>)le`;ivp`A+$rjIiYj%%ln+^)m7-QrNIVd$6~ri!uCNSDT3sUcAB#fuV=COg3E#nD71)BK?H zm3|w$x?9hIpDhs1)g4V=%>L?EKf(?at~V>3qQBm2-RmWGc7oBKsVebLW%OMmS_oge z44j?c5iUM$#PqTH)=&3v;^4c++%di2EK6*XNS9XJ(`TG^1*ZXu@b@Kfwp2e2eQ5l? zi7Ub7?uor*C!I%&0hw@XEO%VN1n4|g6rZJF%~O?aqV`ypuz22KlRelC23>pzKe(wM zFDJXS^y{Sy9pgZo2bR8H(-!QbzdU7KGmIT&moi51@<+EF;R@MwEV!&pTK+ z>NH<^#7_-W{_0BW$_9o4V4!dSDP3`SD=`*CfoK~{@%X|SrF;M)9)7PO z?Sl;=PxeS5UqPV$J<6N2w&AY+%G!DRK;)YfJHy|+Km0HX#{Ay%bK z_nTqu{{dw{n!h64EBr?IRQQ{yiWiHQiN6v*5I>MW(xd^>SZR)Qh14OvCG+w)d6s;J ze5QQ1yc0*kh~X~Sj?%FW@ALq0$HI;9I{7ucOSbY(?oKk6yOmr?EW8)y@z;`9#YVV* ztL56sXwJ=lLza{2@HG${$ zz*g>adL79r+=^FQhU4sfG7w|4VLsfzIpGDg?#_cn@HpHcDcnZv_YDx`8lW#3LHmaL z0zO6Qu#q}A5#^+l+$=8PZX_%Dk2w=eCY{`Sq`$ZXX5q+w7TLiy@h^})D0vSdzci9L zTn#w^I^jcd6Zw#v1f#ig;AUaA_y&206p$uy4)Pxe@AD)0S)7A=@E;~)?uGkM5?+E) z{IhT@xfG@UC9VKQa0}pi{$cW0xR0DB%;x7{EthiwIR|CoF4)002r3MQ`}q6d*W@<- zcccLB6_%5e$z_S5ovrY#v{kr=-zHWHfyDFiKayL>D~a9QS5TFBo}Uz-O|BDsD3_<9 z^qzt(q{5we{B@`?wt@j;*(i?}Bfq*(w<##)!%%aLg5${7C^6??D^-$QXyP(q9ydr1 zNlpMc2kuBvNj@3!$RC8;P(SPz776DF-_g#eo~ei%-lYs9w{he_94IA_xvK3T3LT{Q zJ|2uRjoeSbXOKj?p97u@*`ZARH4b3k)ppi4Y5QtNch*83#a<1aa{@_>)nGN5N38*+kb4LL}%QjSn>Bu=&h9D8L)Wn$1nr z3Rg%0)ElKxMMlMDW=TkneJ3*#jNiMVK1y zxZa-2j}$t%H8Hb{27Hz*+slS$<>E=7iD|i6b}_d)*P-R+RvRyvgjOB30}5&jv`5j= zC@I)i3tFeGro?8iDbe=rDbV&n=La-_d0J=d9)b-%dT*2V-RQlTxz~0u0i-qbC$1?j zI9+>EuC>(G8Yq#qu9%2j1dCcps~C;uRJq+AM>HoI?OTymUgoJPmoY=e9lYD^a!OJp zGe?d%98pPfIo%H4UD>xHN}j&-ab>46Z~T&FeMdGC?hz?do8ee{o$YQ*Q{&v{uUhl& zB}RMJ^gqrVeQrbJjSJRvOf+YFcBi`h%5*NTyrBQ)i8p*hnxcW#@OSF)=ux73=oZYI{5PmeiIY#jHN11tKXYq+%dnUWV<$VY@`DobK|nO4=Zi zOiB8e`!a+FxBN6R0oe`Ba|T~@$+X*-owM$my1sR@``1_XJ7FNV=kIsSs%1MWCFO75 z3q^T%oIf_YVcp^coO;C>6NgQEbM%mg*5~)Xc|iW6{fCAL}? zN=<(v#LjCAEex#=U2D2BBF>4BNH!_2sPJWHr>SZmqcIvvOUuNRzg$(zpiiGjBr`2L zEj_zl?c>A(*->vrMW!+8#FCH}az@1TKE{fuSE%x3yUW!QYt(D3P|LG@hV;VBNS`za zWr+RK)A_=XgY*mWYDCSPm9Z;*W1kN4(GJPol-EIykC{rEtc$9eBwy|P#mS!7yAL;Q z-@eX0XddsaqwH<>)Yy=nJvD^#G-YiMORljn?l!C`_7)g0LbT`BXqG1_kJB*uGdyWv zG354EvZytiGBdJyWmRPrGCW2qS$|2g+f!NPp@AGFxHs8pE+>kGBvZC8FXMkWefvqz z?wdMg=$Z=Ea@<`rFS`5kx6WBHV(<-vmkyeCEX$uQQazBa78J4N3T!L`Ih?dEA`X;)o(QIj zm;27;cM$ux-^M>ji}3FFIKBzF$pV!z>-CnqEu5@@-PFVxevmuJw>vn?52(BM-Ou@~ z^H(N2llJ6ZykK1&Ju~5K6?Sa*atSz><{%@lKlAL#4_~x2{?oF>@o}V?%=?<$_~P0( zPmlMDk9;S-Gkyb*#AWeuTm`%8u$XPGkz8ik44W;yMMFRTpzFp=rY34^m2f=g*E-0A zZ4I;%-?ffr4Aq&cL@Tk|oSZDlkxH=l)p&B{4QHNx%tIF~A=1*t;_LC-;#YnXZ+mI& zn`e-x$^Gw=kz^X)`)B-g{w%D87fy|hGb$#FRkO*GN;ofl{vw0G8$5QiEK4GNBEufY zm`$ovka+_!N}@`?k?GW&A++#LmqbuEO2y*Fv3TN}eJ`!0V`wX5_}b3a+M1GD?3pCD zvycXTN*z^hPc)}olDoMS`7#HY__N|o4GwY%@0@*J@tG?JELh%e%*g6xOUlj??!LG> z@BaGfS5y>UoNwv7cG8%&7mb{>q1cC)?Gt~K%t5P0fu@)(OYSRUizvK75P6VJlsmYy zw=2MRaM#4_97l$Nk-wK4O74Zx#`z!8+Oo>xNWWLBMU9D7wby|59BtlmJ)!;#U#cebR_81)X|1ux(g$a>aP!qUj#>V;w6?(X z^b;~xajW=s+&ccUv|FSO-V+Q#dYVlWc`4wR1R-b+ImJ*A&6b^EFonL0XuHwuv!Nw| zE^Vf<&g+j{aL)lvfdca(v*qe&?|Qm*12#%)`9Q9UYR{C0L>{CXls1thV$X-=<$>v2 zKDoG}G1IeQ&g^T#+x$O0c=`*o=EUO9o_|i5+ZZ`#+olH>RW>!2*Ur3X!wvo?{xs*I z8JDh^`2C4%Yq!!KUxz(D7uzBOis7c1rLQzh;?6H3UIR7*-;wy;4y&z#!!V{`BsYZp z9j2*a7+LCD;$M-qtY}@9m?jt6Y77mAIfnCzM8IHIZ54)S(2yBYzat~z)j)p8Aqt^@ znOq;NvtAFrl4zaVU%Fx!$%g}5f^s^th1R+6yS=O(3X+V@Sfrp8T2U|=OH?s42}hJF zY!7=E$3z|7HUPGkg}3!mTl<=EqII`@`eJ@&z6GaD~I zaoCE1wM(Y=A!BW;=1e*F`}%%UmcISNwqM1+`Ry%#D}>r*KL{g!n}zaN5oFM zk+#=TBtap{?DstE(4j;UhYC!zxZ}1zslPnpmqN16uG7&C>yp$oE zoU&}O6tf5HDNf+til=SpfiWN%ebn~!0g=pXzHjYl} z){=siP1={lThdyva`aai7Eq6Xxdkh=uW5cK?Y!E8PN=Ob!11h>TB)@-sIL*39Ylw9 zHfG4RwMk(uC*d+r2FI+Mq#chLNmdN=;j*fM9F^g43mUagpH_Rqnvv&SaO$!PKe#A< z56S!OgZ-wRAAfc)X*#dCb;;nf?vJk*cN=Y!E^ArQQJQo8+!gPx;2YMSJ#b3iPnRj$ z=re}So`-sDcj6=QO&llFVAGB(4aCurc%O1i>ib(O`PUn-LlnaC9Z%aHVn zAyi~P)b_P58jbcwipjBgTKAyf+K_W_gi5Ngq}-w{=#d~20-NTj43`Pi*@=W?$;PBr zc>VEXLQ;_>}t7%3&|3IEZLuiG|V z|HaGk5B~i5(^MX(B4=$!&ay%XE{%=!gzzQ0&!=`P$TU*sS+OeJMx_oj)<@0dLA?uXAhVMF}) zA78(6RORq~qo$tLHvGzXr?}e_-1Pb7+v4xMb9#nnYq~vBH1dR>HgA6<=)vAXKVIC1 zcdx>eu>l2K9^a3P$tQ54k>@y7G$?|>Y!VbDb)%9PMztaXFND#q<%~)=R|SLoI7me5 z;EsPl6a|HUoZ~PaOIHLuP+0{iO5Gx4N{Z0DzZW4UHlwv}Ia*1x_b_qV*Se=mj2Jyw zil~Z50Yb&{B(rBoSVmc4zhg);PD7j^V9%=M8x0 z3fvj143eObX5h>DN-4qpBnkA+MT2Vmr?;-;;(d3Jr8_iw>*VpBqNoXY>llrB1wm6* zEulT0>V6~b`+2PIS=Un*7~P2pZQmY^276kQHvd7lnBoQIMx~U*0XNu6%J^N8PIVg3 zNA`DKNbjxa*3Q{@XU`-)lENs}A$V~YWT4#IP?U!UNk+bfs!R)$=8tz!KBrPW2qUU9 z&FKvEcMa1-AenwX2;q$- zOn-itQdFDR7q1(;J>4(H%PbC<9&BBe`gQ+hA9Up>GIU1%owTx z3P@F$mxM?tCu-B|S=5MOY9LeKn7dcy;D<-Jvrk_UfBwq&qc@!QS2F#r>wY*l{n6kP zS8s}ccFP|gedLdWxxrgI?;O?k*v$lpfo%KDqKALwy6VQe<2S$i&Y#a9{V88e29OL) zBHoLQ6cmF&^l)iMuTg@?;yiY5|;)QOzfk$7}RG{1s{-Uth0IZjsv$MdOXqV2(BTi|{3{APCF z5VJ?>0BBj8KeIx^v)>>vy>P=-2j~UtaJ;v_*9=K1Dd@VdKBag76N_@P6mKE^GPND> zjN@?}Dq|k`(wO()>R@l>ieL~Q7dl8@EKO!rJGe`vq?+1A^5FkRQ&AP(t)C947OhZN z9#6`ijDN-b4K>e?pE0d71-a`-te2M?9jn{qCzX7aT%}ZM!{y=1Fl~grg`a9aiJvdb zHOy1yn&z4p*ylPH_*dA^^k2ZAXIp2#-TtQiJ%8FJ|9gIJn?x6v+-C&8Pox5EGSeP- zCFVy7w`gjh!WBazx-2{p70n$3;jpH4wY8KBD4#J2OWOt3#QWPVA?cySqafl>z@CZl z0&=q?BY&IB7K@4ww9Vphx?Fa*$Ln>iwHOri-)8O*?Y2+{$&HP7p<&61Vg{PhcF|yW z88B|MIBgRuH)17Lq3m6zgv5;^*CrBC^!Y8S%d4p#??IC1qr!t@FB^mkR{n`n?dwG= zwbn(C(N^PeFcj&?zxO#ktZU$t1$8YkmqT`|9#^WxxiG`!@daV^{Z`Uf0_B z?!%Wk6`%JBargd_58icoC%+yg{kiW+`G#Be5904S>#oNZ?QcPDoq^nHL2h-B>9M&> z_+{b>3twYtvCI_~~73Du@i zcH(YMGn+GH4xJAUyPBgzsXWExFl$q|T(il9eQ!2*kRM}y6ROHp1Ut+`&#IVAKPhrd zUX5;A2RT2M2MS(*8R-s3kA<$t&RhaU8HHE&;PqIqVyh=RmJUHE$X!NzvR{*WG}Vt^ zwX%@yp_x4eQdxgabt|y0)M#JU)v}Y*{_Jspy5>079Mok=fxTY~8BG;34!zqkgn|Ov zh3J{KCi{@~B2~(Sb>9q9{_F1-4UU$j<=yv4{N=~vmrtv-7VJj#{D=K_&A4ONQ~Ys1 z4&%qXf9Hl*_MeAs@F0%;#W?nB5QHmZ{v0k>|z_ zJ^tPmkIj2y>7o~JZ2k7W596D;S!6BwAbxH9jo&?R!84T&w~?aFXT7;{-Xp}b=4~Rx zPo-9UGg^07Fst4V*T$-E6K-{Ig^qrX<&F#Nf<^ON9hyaRr8zj8-)c>9sZ9%`sso>& zS$J0DMSll*B$nyQFOlk`u~M6~P+BcX(p+=|mJqJnx|@rJU2D>+|E_g(=ZAl1$#olY zDUYuP-Q|=y2h^TpvoRJr{Sf9I;jYPta5E|AW^CFtXXK(KgD=_`zvQ${WXMD9Csxn8 zB)(SMJ!0F*Qy*G6&>HT%oBP|it*s4H3$Q8=Cz_>FHt$&s-^83Pd2_{C{&W38rLPoq zAhrC$wBNO47*jAKgK|SQP1PeiNZD!rvZq7&$+P8n&dmD}#3*2u^H%wnh5rgt zE%f7ReM1X`qQU|`;>+VXcR=7l^yTFg78P`mzA;-4U&PhO!6FBrQzR>ae0Pe2+$j!n zcXN=NxW|=7@=Hym#6(OLC5Pak&g6v7KC~P6+UTr;z3ah(jZUCzf2joibiW{KFMj z?%Q^G{PQi>EWcp>F;m-y51PBIdHj-NP8&9UD-mya09x~Y~j!_96Q#HHY>h@-N(!6em zA_lV^?ktZd#fx_2N{i~mQg*A#XIr8{)$UQwcc-;^!^kf#h-5J?^;mPQ$g_s5EXCvL zGGcI)y+2zC=7*$GiIavzsWG!*NYawQX2r(+C&p!Jj5^p4*`s}svc~Bnh~feZ)oWr> z&q2=|M%Oc(p1BM*i6K&r=AeTSGIfM9cyb;CIlK)yeD=8uH)eGOATQj0; z&eA<=ZztA=Y8G7F)bF@O@l0{|fZJB@_$<=D=B#z`&xq~vlNw7pm-6|d+H~%y*zuXv zs@jFI7l`bLV}yj0XzA*+DeBIca@kiFE-S?vq$oV|l9 z-%+jP_n}N}#7p&gCOzNZ7NAKjGq)lXC=GBWfmZ_W2lzk-cj1njXR#}^Z_&Wm*SW6% z*}8Sn0aGCKiSZt7Px2`O^L^;hfPte*PU^|N6*v-rx@9rJ^Avmjkdjj zg0S&XuIawh+IRISd++YL>BpV+#g#k0TRDyhGks`Hbz5WZpsP<nr}3{G@%X#1kg%1l`!8nCMjG2;`4-Nb{Z{UK%&Lu6HqyGeXd4W;hG=hr&D`}}(` z-0R)r+w1i+TOv8P`uFDH0j40mk8qE%$9dx)K72=s<=Fn5`uzSsqCt1%=a)WoGXE&` zg6LWi!x?x#8RXcQH`hZNV1%z3T6}Y1u5Sgb@U4Xle8!|z@_>hgg6CsP)2>i+D-`;4 z#)Xlz7)_;V=tOwI;4(2M0_&M_B3hG9L~OLrpO)rLz0kxdR*TuJsV0+RN1s7U&oHHB zWMn8Nr=plVn#+~p_8>ERGSU=JNK4ZcKEv%o@0!c-dJL{rF?R z5veEA)b{Q+)i6k@ttZzU7-3+M#&+L?=3S7Mcz>rBQbJf)IK8Cgv=TJEt6Nf*TbO5t z8_FT=6V^3JOPrbDgiM+8OeQaLWMw7!_49oUyuEltgxvIOr~9=&vM?YoE}%JAQ1M~R zZyW#PEPmAfTQB=f`OKS#@%_-WTYk#c3rFv7KsHJ|g4~&dz3Cz+#rg>$wI(DCF|Q+h zjj^9vV`@+vO!bx_*2(H*Q-}HkeCyoj`r2*b`6yJH)~XknZZm(U%8E*o{T(w}IFH-z zMGw(HyPEoo#BMeSbQK(D1p{NoWGAP(crlyoP*o5_Dh)=9h1YC$yVGjr9Co{g9c@CL zXmS~Ni;BXea#k~MahVKukK179Tq^p0E)y-q(ZP+!!HqXRK?rYBkz-s|9K~Eyz&gbe zQ>U0h{1h%`p*{a}%oO4jYG7XH{`7eioTkxq2F?41pZMq;nxA;Jz1Y`#{TMPCO7=G@ z(!D9eDzj*;K*yqU;>@}$)&&jJW^cQ6ras~!2LFU-#P9+^)qfJ6FODghst-v`1mNQ zWxl|EEWv&>!3VMNbCfwo4qYcjQc13AQjMmr2`1Gj@x}~ALYa{y!bu93!Ank_mlTQP z1&djg2yaxd>BzA-#Gy`AHPb58lpW;Ron{O-JfEC!r1Nd0GdXQS+sVtCDD}V^8_0k{ zATo!gJ3>{Cz2*R)WfvF{uk3IKsN2FqDHR%04|C;Ii1{CH$1t^!nF&bP(W9?;u69{` zz-w;~A2M<^Y5t_Melb_YpA(uTXmfUuCt?{TAt_pE4P`*H7FihKB8iNc&0YZ+TE>D5KI0*7Ihqqu zr0iKmYr*L6T9LhytB+dQa9y{zvvm($lVRy%t}X^kEd@yVb=`|tP%UItvGKe6Ni5SE zJPzBQNYsH2ktI2qSy{&VM@~0{dgLn0Jsti4Y!>z0|eNcDrqtP0F*CWY=X6cTIN9 za!D)0ghzdz>A*_b`9Kyg1sxn0A>hQJhC|Pr&k6=5*^vuIZ_uhLA$whbK%gX07Z?-x zA|M6=)Sof|y7pbZdk3kE`4r|s*1;G!o-UQ2oW%zG$us|M?(LL+aN50HGcm1;h8)|x zV_|?)6|}Y_XO=0$ckbz#lk0xowkE0GQ;%4vsanpbmK9le?5G^sA+tW{UNulp%I=>( zdHtv>o*BFBjB^Lf-CUGEpPW7I_)W9UI{t($Re7ks_l+6!?i&|iL&nb^TJ5_{G@PZsg}QSRXUi@N=EB)2@;P_DpROeS&Gc(8e8`Hdi}MS)pBR z5GAMEnWqdP&0MoVvSwK(s3fZt-HDCVA`x_#v(aAq=bZxg|5lQSw^uRDKpl(8p&Ye zNSEZbT1~3zR7{W-RAj5g=nQhI`9({!=|#J7dtixeeEBfA5XQ$)L@TaQ%NAMjl?UdeZ7Uemvviei#0ZO4y0m z9)qygeUJ%{?t(D&xUoTk)DLviR%xcKBIRaY=)KS0IOzQXnhY`YG77$V3IuwlKhccF+rJwt_JRGU9>0ZUwdkvE1;}`qadC2^iX#q z+RZFZIhyV*OExE;$nitai*v)%u@fUQ$t8Jejr^YDJ*Vg; zQG2DG7f{6rF5d2PyKNW;(WIJqRk7II?g)rDYKmhl#1c}7lPl!WCJ_W)a-%kKEOGKn zG&IfaOI$9+?QRA|Sb}~BwJM;4bKd7sURaOX<^ZpF&jDV=9+aDst_O5%?E_tEvxq8| zF4b?Z(Vmkv<@YX1kqBEnb`-<5GrG2?6PRXK0r zl5Fd6dfYu)-NN;IfXmG8)3l|bB-eh?rW@b7^Rv@mU6i%;cac+GIB)f?sUNvA7Sy-2 z&%b2e;M3++wb=#^w7DnMJTiI1-nVv;!mFRY=cmN&56>BV=6D}BVSd%9A_UiJZO^y zRj_bQ)rmUCa5(4}lfayegf}#x-FftoJ*BWe%Q~b9cM>Q^R zf7AG#c}3qUcg(&H3mM&-gZ}c34Bs-5#;9yxH#PO4rd~gdtKl4hzBjHl996V~o_&ip zUm;z1Xj^1u*|PnfrC1FAYGT>6~ZY*OX;P%mSBq<;@vaT zirHgAaeOiFo)X7W2WG^}xyF)c$h9Vd7vPX@MP|rhM7kW>l%j%_7L80`sr{kR#p6C_ zod2^vQUn$CPy4NdXjFdEDW#XaO%~|laCyqXl##MihfeXQUwGliKtBG*wOR$@Dz8dIILk&Wm$ zbeCHP_I10w*W0*cbS(#{)+ZAUxkCbqk&kGI5{;#d5qXdTV##~QY_%v)4uMF>MNZN1ZR(e((60sIRkK=hzp#FXleyZ*A&wW21JCac)4H@qDLRz%kkwV~*Bktk9~R z6!2PuIh#vLCL}qTQW8t%f&sd6gCnD&9Cpbn(F_UO?UE@Vz!7M*T4{zA2L@W>?CMav zUeBb0I@umY4R7ZW{EFLPH3uXG#ik>;p*Ag=9ZJF(LoJ0j-fdDuKA z4;oXTVdDj*pJX zgcK?iQb1L@G)yGe&L|lCp;tvEw){Ut_Yl3H0#bLN9Up4=9280 zzgZE}!N=d%+t{(VwdK6N%--P!@xigG>H_FX8~V~+ z$KI@VkXUUlsn9l(x%xJ;PP^7}m*r6q(9dgsZ!vPgHo~hpVzaTna+?c!SYIhZ+6)b8WHL(Lal8Mf`vr9oBY7yrSvoR5EKRqG{#lg=MLm)x=}J!k4Oe;EmY0o@7~Zaq-X zh$OW3A!Gr2WH#6%;p`P?0WXc|i6LrLc#t)Un{QnZ)EL!h?+#jOSC^{rfO)lbL)iML zgPbbD&TC*&qccg!otmDF;-f<)q%muHG@PbCCf?&Wn;F|s1T{imkORyD&3Q5!gc+#p zD<1mNv!dr)AH3$Tk9~I4$d}ig^W4fqmvtBREh`VQU*0w6lHMGACC+>P&aa+-aOBB5 zS3Gg~?(5T+ZkX1x@Uoj`?R|;9SNqvW2VV*JSx_|k!5vd>Z{!!776h#{sS1N~?B)DS zQ&o^Ej!!eC1m*EXCYAY!_bc}-Bi&;KZ+^&Hye?!rz6O0Ctqu)|*XQF(yxiO#qSi*^ z01M$FZNy?x#HZP8jFk1e+GaQEEQ!b{NMR^!Nf-$`LZaQVbk;0bETGCSVv?1w`nj5>0=Egm(~7Q^C(0I>8I8@8x3O&;W?8{Rsfoj*tU}mp z)0|>V`E02m?7G9=bY=au&ve~@~Kq;7O>!Sc?{-6-XUJMSQ9&n}+TxTST@xDmZGc5$q?k*?_G2aAGu>9A<+Rl63ND zOf;zJ?9oj3%#4D`G|w#b7+|;pG0kRkvANc~#0;H?Tij;B0<%eTbfUp*wwa?vm{!Ik zfBM$I%!yjDqjdeb9yvW`42{kbsNt$?k@d2z9l!Z!Fqo@s0y=7?(hnBMH`njH>iO%x z-q?IoX42k~7Y~hGyQ#VUj+I-RmrQHQNnCr``!79(-8Zl5`rQwu+_W36xd@xj-*nZK zx@!g$OO#s(lr^Dm-5UgMt8k_Ea)DPHf(^H8pXNX1f8f;dB$3a@xug=W!`rdOXd;}B z0NoVxbDmbMQy9}Et9PIqavROrY_~Ch_E`p9n1I>DQq%*A{wt7IvgG1WqnT;@=N`gE zew#N<%!KSKr3XwP=tFC?qQs3h(9?!sS$U`0Shzn*(cAMC12pA~Hs=_jef$gJ!6)S8 z`4EyHd8S|iAOD99s}t^wd2QtMNF$|J4M;Q&=;cJ;9MUD5V79J& z!oX1u428(mhP}~yW4P7a`q16FtM$B&4s}W4Q?{*M29Yh?#UZ!pZa451+&bMwIxbls z8y(}w*YlEI$3=n-03nZUvBC9$C>G|2e6Y);RQTTlHaX>IjY;7 zO4l;K(n=`K97p25V!P;;*raR}Yz`*x1>@ky(;6R_g=KHb%x{~9KWu_%-)T@3y`w8Y zbODHd0%VaFD3LsU!pauo*bZZ?yunDhQeGFL8|OB*%OQU`G3QXm1&wRh#>S1jpIBP< z%)w_~<{#iU4lZs_jXL*uZe#P)+m2jt0bZ%A>8e;-lAfHrAvCGG^1Oo&U87&x)0mYN zAC|iyd-j^zYZoqD$X3X{AXljFL}BP6ck*;on`yOao9SBPb%A#X_eVSy@kt;kYs{fg z6AI8dEqbtfIGyP$3uxH==7E4AJc0sA_yOxYL4SmV?>7wSj*@U-)^H>Mk#w<6U{Z4`~&PGu9R;-{Fc6P4!*ob1k-v8o61w}iPQML5TVofz&wsl-sLq9{8?1V zHcjdh_Yk$-O8*FjOMOR*B$w@4CoJTq?!K!md&bwszP*1QF1NdzOzFN)4 zgMJ@z4~A+(g{yUkHHXEIgZ>nv5wwD^S-&;pYR%Q+-P~`~+5|B#WVL3sIG`U4Q9E(E zxxi9o;e$d$!0ZSPwFZNgaXxf+a40o9R9dSx1I!LBrn`~a&|pCe^LP`8xzG?%6_ywr zs#Te7^Ma{CVG5a79E#1MwV_KwyF>XGbm&^LF6p zV2Xm#tzf&Y1DcBH2KS7B1WwXdh;DtIe!J!(VWa-n_;&F@Okc_sgZTt8AE{u{?2=#E z1>Cg6C9hk{o3?5Z=G60$)~qE!jUX`F*y`$6nCr&7Fu`8k5T4041L%+%{%h0NW-S#v zTEEFI)0`mM4r;{!TQ*LaFK-XB?GIeH@BW3R;Otqr<ZEM75G(-5Y8Vnkt@d=p2 zFOK}}nFU#iIP0B}saXHaikOsA`6H;VHFJVdTQK|q{KfH)tfpH2sH%eID;ZG7cJUEkW3Yy+?yGk;85 z#8}0Wr>t8sHUT85eAVUW57B7mQUi`=;y_x2=J+;#Sg&dx;9EsL9!YH>y-B+rLM35j>FK%=Uk0gI0)?3Yj7|!HgK8e0i#$zkB>f4(rsu|M1Oj zEWa>FCo0^zba{d=>guVBTf3guff{Wdw;hLayJB zB8@|2FES8~J0S88VuA%n-WPGbCe0dVH#}rKLY#+CXw<3)eIHJDT{nDi4&4f_Q1TKl#4c|sh;lPhZiuZy~I^Nx#)Qm>w08U$7_d8hr} zmV(f@cT0FaK$WM5EZsy{fY$(R;=gRVnkzdT3UQe z8%jU*LP7cq$6uhkZ0YIg=HoAzUw{z+u@`96-?4HyT@KAJ%H<~%{#kjTq}35aPuNXR z!ayaoDWUJaWSYiAOy*ILm`hTN^uc9$-H+UJ48hO*@%6u0R_!@{b(#ke~hab*L z&l#?S*wq;s*+fzbjc6e934QDj*%{es5JBTlFwO+QF!C5$f!*9nq(k8-%^k5$*e;MR z0S|IpII@hx>NG1#(;E}S-~_PSPrrQ{r=LCr1BWimV5^2i$5^uS0;01t+46)W$SQw> zhTVo4BQN~bFax{(dhI}SPL$0e`=n!1>B#w*Oe)0}3~yVKKQPzHdGT1b1nW|LkA9>6xSlsz@lDqItY2EaR$gz_Tg@aP%$y)G02!OW z+~0iKWpUwj7l7+Z2Z#f+nVm|(!+G*{fSUFjT_2#Mjhb_}te8G`Wpzr@Nchb-#O}H# zdD8l~TbnQY%aZP=yQk)o-Ah`gH)W=cysZffj(nry=%owhef{#4_e{wKMYzuUF~5|b z%vJ;H-MMR`w?&h=k;@~=nuu)?WNqk0p=4fAdk|S`y~s+E%s5_+xneWe>^6tJ*bpFT z4Pg>k@((am9EeP}+!Q)9TC*K!;)qEjIoVSPmnDDi8N*%)AV;4+QMdc`EuI@X3u>=^ z)3{{U(v1Fdmt3|qbAX@x#?zOLJlvJH;`-K|!w+};G-b|}PxfxUwBZWycd@g<5T6Hd zr=Yp+$#FQUSeJs(B6X*lsC7D=8dMw{jN?#nP*Ad2QiE~$z=7mqq^IB( z0L{}~mI5HQeB4SwlPle2rab}jttmQs3YRy9%gLrMAsPt?xL8*f{b=66u}@x96nOZNl$jk}dcA1=NGqSuPsSP);e82oy>357fsDF2 zISB46ClL@e3Pd!me6tVfAeQ!hx2|TtRz=sc-cjADdKDF-S?F%}@{U@(c{*M_9S_Ff zcAAQt24}w zO^%D3mTWg*LtHG_?wQuqNp81jPfh0Pa;sCp9;I(t6}wJnWfe2}4rgUqTrXx}U<-xJ zz~2^E)4AJ@feodSKjCU}jj;^{n$7QVAe5>cxleWTTSHH0Rkq=S@UDUL`h9y__HAoMntiJV)OU`+qH^*$s zw?$|P#Wm0T>O>x3+?=9+WTTqp2vAN8tb&i60 zQzXZX!TOZ$+g4_$FS>YP?zxpCe_yh5Wv!G{PTtUmhKA}4&V2XOlE}0fxl!4kD;7gf zJm~#c^$qky0&<~k?gh)U)?{tV;tJEIr;&?d@fHcMmT-cEx7qP(JDwYW8!foNfD1I( zL-5W7yf9%y0;x*Ci3vCXp-^jbk~1+ZGMRpvh!T>b!@^YQsU|KaRVA>2NZ%8aX`Ypp z?qVJ#>v$qJVM<^+Fat(en`olx0ZR%YadfV4#Zk2*ZS|ff&)IzPhIwmW#r8Y5ym)C9 zCz$jbz#*ob9~};(t2KZRh#J2NvFSc`v?s>8*oP>#jYu>uEgeuRE%CJaukp zZfQfFy)fK!eMfHk{PXH?M^t{;p@%1}IYcEq{kq|G_JwD!yCh1Iq7bnnl1HWy`l|xQ z7>iDCFw-w>j3%qmRB2jkB1xtK6S0}F$rxa<5KU^7jWe5wWI#sX!DGjc6(6IFD9cm8 zcUy|`^@{441=u|VGPlWQS#;niq8iSDT^g4ki}6zr7y@p-X48@LtAp=)4CfzC(+Y*VRAuCwkg3s0bko@S2Ps zba&+GaY|}xR9c2LEh8f>4H*rA0VeuhyxC$kTUJ|er3EKhz-q%5vl-c==0E(W2b+lHTm{=E)J` zC~7b7t}E@VPE1;K^{Slp(|&?t?!ki6#goG2Vw8gpct2KWsD^-p`rK32iWiAwn;Lk- z7)7_5^*W2$FxRl$KnymzzZ?~V2#Jc4td!M^1|$(557|s+Nq@G}LN!|B0CgZqd9Gr~j#^bGVI3aF%9N7|!8)I=wG!Bc#+GspI9EXJC?KZr^hIa(x z4Z(OtFwQYxk7iJ_MZ;}Y<27pRQR7J*-av36@U?`uhhmL!jgf>JO&O+)*;#_v^*nOIfogMNjR! ze!)lg-(S>wtH;y)=o_RBZ~yu)P51oe>YnHKUzI;{;@Rk!#lQLNXvVsozgn>Bfpg2A zd~)P)({C=QPNOfn$nU1D=BFSlN=BEs7p{rNt3$SikhvktL&z2rPB7t%_1LM$q8{sX z;kt`;T#|@&Vz@{eF%Ap}a9FHX3!I)X4SePTV*qWBE(@!Z*T~oZRm?RY!6n7MJ#VFOBN>Wp&BFUVK zbKS-2DFOCLn)FnaJyRDGX16=yO-Ql9TrO~99R~wJkzHffmy2ON-Zb!;0HZRUKO+JAiQ^4 zXJuSWxu>lBf|8QumGPOgI|m0x#0lg*uW;FA1zoqa@BZOHOK11*=n42R6ppJ>K{7VF zQ$<10X*Ea#vQdM4onvq>LAUN>+qP{xJGQMI+qP}nJGOUh?bx>MwdUZ zznbY@T|L#`W~O_s=Scvqs==ySscgV`z>_fWA?7KQE?%x&grcLIn7av3Em*uuPdqjr z{iljB782=J-{OR+M?0GN9?wtk=JECQAwfd8_pBS!Lb_uh>#FcErrU~<7h3B)L;tVO zx!3{xLxMv*X~GZ>SPwhP5+1&xu=?L@2T|2umIiFND_L8&q!cX7vL$Rdgc^&bsG=_Y z>`bfp1X-g(9;MtAi~7};2r9n!-x#d6MB;^MODMr9*ei0f+}K(#@_0WfXxy#xQ`2)n ziy4)TZ}(>tPbLbcBs!h7)pVEId|PJGnqH@f!mQKW^+qFUX(D-@-j*^_H$fG^@wpM% zJUeQBuzIt0va~dEwbxYBJ8}8%ckia|)wJ;p1_Ja<6TcA|v#|M%2YqNd>;gR}kNz!B ziU(h3Zs>W0T6ae`liSLXSfe=VbLr!^vd8>`9vsKOv+FNBCSzTz7{Og^hv7jDMKd?S zaxVQHvL8Le`P(?E1Op6>9=!5&Q9jQj=Q-O6=t6R+9F1OoL>A1Q|pU3~~DK~UaMVrCfMwBs6y{Dp-%1pYr>bYn+C}Ur&Q~gqF zAt#k%sl{ZndWrEyPjk`rGbxL#M{wG;vL)jT5a^mEx20;^`Zf zobsPh>dTIyypG&S{dRbOM$)T(Ng($%f_ud{7S%uwV*=u#XS%elT7??TQ)vvlI*_$R zggQ`^(WM9uEnhGa6>A(<3q>*3SXFS4;p1QkfG9puT9pF&{w;45jd?elygUORu37}% zx2S^tpQEKN!m1aOf}|IT|Io)Q>|tND7Pgh})B{fyJ@6>ebwRWT$W7aq5U-*1jMm;D)Q!@-rCGA}OuH z{(Fh_xKp9EG^Nqla|@wq%-G|<+8AoKnUcB8QPI8h)a*u)c_h&`-tiHfN9udZ2eks`+Y@|8{{wS^*2=R6HO<+=1;C8fELj|nEuQh#8gQq{Zr;boX{-)L@$F zJr`?=i}<2mo0~npO#%e>3=F~3JevywA|)|1%X-n*JOm+JgQ8Y++BXDqPm7Of9JC}a z+($!MXhY3&hozGjNvkK&j}HIul=mv?eNZ8I$3c&Ia7ksnHDk|pVSLi(W{PxXi_a+= zWJWqb0;oGb1}w160n0JtfQ<*eS>8Q|O}k3GN^S+sIwFiqjRxM$3OMJ}3Vz|U7YrUW zd_oZ-jFc&>4rg0Un#4FvP?P%+K7F8@@BVR$RNDf#_-UI_)ob_AqrT5PZNsrPSKb9< zz=aZuadyuwQQMuE#7^Vj?Y3^fv7sT68Ccn{fRq3(1uGn#jvp=9_%{kEc@u^;=s_+M zK;#m_@I;h33Ez>&Y=YS$+yJlTU<9qVtDGaR?cg*V-cN?t_@5kR!b8zR(Oa&LgkP7( z?&T)UaJ zjhCyP$>5QK%g!zCOhO%DR}ABFM;VgvqEe?!^X~?Op*I&_nCw8@`iVYE_O7rKd|8VZ z;UE32d+mNHF8NZ_3d0`%976$du_luAY>LL1M#Fx6{-{5r{M;aGa|&H|k}tPk*M)WA zX-Qe75)oV<_&dDH0r_c0!~T$(+=ou#4rq!K_5EPhQ0l8`#c-WF=J@*!VVblI)uB;Z zGlLPwX~MFhQJY3I&r9GIIDIqzNcHOow=nO7*<6aIVUYFQFZE{^h3P`4k6$UZF&s;{ zKl>1Xfo|(g^t$H#U$i~MKsulxy7zOF8t1p_`1-|z>U!02iBAG;Vm?$RKnL(rBybnJ0@@HX#v!t;+^opD538H*P9d@rLQN{YwIHj z)54vsOkAtS6QzvUxBlxd8D6gs(eC%7&%U28z{0UoLam^LfWXezGGV(Lg_;7x;HGq` z=OeMh6b0?iv3954PI|(Fj=x7djo^FiP~J$-cT^Y}(K=!*soHSdI5`FV!Z`KBOc9)z z{puq1;4ky`$xNn(18?o_>r^i&^0W>Ej867}ML`q^!{+Sl%n!dwptFjw!9mW}xEb{M zezvv*eZE)T?_xjpJ~xB%?H~y{a&?5RPT{u~Dh(3^gsH%7ofx$fy96@nWJh3=)XAlrHKcAEi; zI)9`uFIN1PA7+=&rq`@%{R&*C=H$@v5&hceOTFH*2sp)yNU)t7VNibW<-1p_d_R z#agicl)xD`Bx1?QcMB{UJ}%^|PUHwA#}rci-rwNoRqnjFc;^0vQZjZq{ri933QAD1_8Sp$N!;S+-6UhMl zSmIRSsqB7QtN1&N+cbxtgM*5hp@Gj)o+KX&-?RNy*Yaj?y`_={aPk4H?x?b8= zoAJqS>wdX1`JaSH`w8iE?{xZ@jtq4r2>SQd1Z-64U7dGjzhWaBlHtIsx{s{E)ott} zaE7F3m&cSh@aa%X*P|*umAahLM-L$@(XMW?KQ2x>vI8k-*@W)5nDcRUTiw2T;x2!W zM0B*m&<>0P@?V9b*=0M2laMp(O~l-L1P6&idG*1sp&%{*eC~IDXRiuFiXVcAXklRB zP>@)pjvep_7{sk@-TE1z5R}Z(UH`f?V9`wynK(m$K{#nvp?V*>*SDyiS7CleuZXv6&ybF%3Khy9<3H${<#n>FaXDdo6%vykH#EfB`k$ z^(2TVM|-b)UBvh|5yP0`2Z0uwSX&r!5H=o6SoRD1S9o!pM_DwoIa1Bi(BMohntMY$ zCnA)NMPSY#Qj%jiCh0{*zbbf98K;NNGKVu;GIh;>KS9!f=2ZV|Qg!l=V4n|7E2i?N zXKgV*E9S3lk=fs&*AGL{0J6l*_vHFykV<*0QCo{{GtO+I_kDOrpf5G8*9_$Xw9*I*Zw*G<(0ouUkdDkoErB#S?|A7SA=ZYM2RDs{s(M*CtnHjj2f4T@XtyK zD|jldzsEMIM%oaxzL!Uw15eP&=kNpT8u|Jtf4(E#{*RY6>f?!&1uITV`vMK=>K@z# zg;>fQvPA>eKk=$Ho5if3e1rB$$wS1FV)AHg9DgPnrhj%rSq)MEW^6dIJ*o};;+V+T z$_6c-lePy)uQGM>nOZGhofSYf#xRa6Cc#<}E5y-1T0Or#y0W+Z;@7R5uj@4I>73Au z$;LGE_A@*Ppu(Y2ISQ zY}{JOqMSw*V?}L1Baj2Yn&wVfPHP~#`|HKRH(aV*f0ems*dl|}Esus`!&9Y9VB_}# zm`O$Y)30?);@9KA8@Gt+da}F$E1F4#LVsNC#S82Lxr01ADo*b8sRrsLtA-b3mdiXgGKxOK6wnt?%1<{nOa9W6O(ED*D`Q}~Xclcy(Frd*Z@PX{-N|wr&FqSCx z+PWtQ%y}7d{PpiLy5$Q@t1P4e~A`TQQtW9$Oh6dG}>$CTln8g{U%FX3TQu(i|CN0m!{8Y%J4q=hlq| z-z;v(QQ67yOpL)PB^e_f7zde=Q0x*u&=a%Wpcv{4=GvilpaT~^U-GTUOgnwUp8XmqP;Vz-(K4)WgF(-biY0wJsXWF`{cI7ETT5W`J`=6&0X| z?9&a!OMIcR0_{Yz#dWg|%}d8h zVk_R_Z}E1lA+6)Aaxqm4Qe|Sby~9__2RHrOXp>yoW(rP5F_gt2F->wJ{GGhsa14 z=$WIRWRp=IrOsrf6QCPB?oc{s;j`5|sB5}yzHJWO3J2LmxXSs}IHJ7+lZE~0bz8Z6 zV^bGm13zW9a$w{hq7gzEr-^JC-Nk5Sx+s;wnA@74RvU@&-P_3#r_cHQpb-PObbaRW z&h4JoH?M12S-beZy5HB6M%>H3A{b>C&R4AqUW-sC5dBO;x1aG#%V;?}s-k({kMBs=Tq&@Y6Rc-YN!Curk)tys3G) zJppZyIFhd`vAI5y7z}1Z0_DS`woKl?^iGATK5sX{J|&V+E>AI z!2N%%?enGsiMI{77ye7O3%NoGeqXWrh3HjmJDCc@5h-5GbKC6}wyU#`(d12x&Rigx z@!l^mso+rRR*rNh*xtyGE=9`x?W{V?MO~C<(E;g$#2))Kdbc*ZW&iig-_<6DXBz}Q zSCkKsV@#G_DCuKjRKDzRx_|K@NuJd{1QS6lKZYnr8ce-OZ%aT%1eNA@+|?$e(KE(( zV!N0^xr=Q~f+*+I0X*)31`zZZLi~$$W^Ye)0^!3a%=y2SpxE)ib_PB)(^N)2q>ojw zqOjPbus+1#zDO)Ch+dm(NT_xY{K%sPL?DRGJ&(Xo0(ENy=k8_dfo9_2)lpIagFfhq(~W1l9%zi8;k>FZ&s zSh%M6$wCPty?O||SyZij2wq&@^`E-{6bG0<@es4JmVw4TSVb^NDpzIH<(4@Vy-BmFFmC_U?TXK$}u<$>?$yec}7@gB!V}UD}I3Fxv@R! zNU(6o^2-QALj~}wt7a&W0(n$RcksSfZJ+HM6=P-oL$V4b{6msHdI29m#UK(n`Ivsd z@_E!Q>(9-wU=a5|T?qQIkG$Go?Xy|S{K`V`SuHYFp465~YBAs*GJG!qfh@<^!f%R*WcS$-Y z?K^NdSgWUGhl-D}AA@`wthG-E{_j z!DK0+EQ%CAAN}kFZij&m6zAf4yAZQHi-S(m#7mHl=bWN`khF;q?$U~sx+s<5T}QVc zK@2-8diW>hnMvejt?vTJEUrISt z_&ityxxG*dxW%C%e?+{nHw!zaVo(koQ!dWbnNl@rgB{vJ_heLT6<=^XTH}x4L@Lz9 zxuNfUKb63ll+Io1PyS-rQZ}6MSBF5>1aVN8>SQufBfU*X)AbTMQGI%edpp1(diett zoCZ+72f~n7m&7C@Mw>yP9tEf`lZ1Mb57u7o?B_+2!;ZE{ds=YM7ALom0>eYB;%9zE z-nu{zmU*W{Ngd?9Fz$>|tqVrE5;V(q_*MIyx8Jh@XMZ;ti<(?tzP{$O2>4Ay8h5LX za2dg;T!DDa`q>0miS|>c&h_B#o*B|F*7IH^h*qr40RF@|Did}t^p9-W+>Sbwu}y;E zvJiSbo*V@1^F!4?GSQj(Y+!YPR(~I};>g&5)fuy;S~4-M*Y(V^)nz+kmI{Q95AIr< z9(kpFxwEXz4zHgXDNfF`Qnh%bxUg1rh8xwxNW<5AKUSE+AsG0}UuFXzL1(G}{L&UU zqJo{-_(yOB%j6Md+6GwZ6WnNgL`I7@Yn_{OBeBN#Tf^omLSu7S=WJqwEFp1Ka%~=8 z#R0$87tDFI=#VEUV-J>CR~QCwZ|0Z%$zETZ;_rImcYM11Dd2o+zVT|M{P={J6CJS) zuii|I332%^u)b%T2s$%{EWX5KO}DKHM%C`eZq$9SX}RT172`=FshX=KByw~S>7|ag z1oxG+tlX*jS=Br3LtLm-uZD{uQ} z$k&Gw6UNARhntX&`_C@rajcEWlXVD|tELhpsDIpJU$0APT^TZo$dgm%cq!-s64b}R z#|Cqv-zA&rBovEES5=}>ct^+R36w9%@z#8@=592A-!Yxvi3|^pM~oM&>&~vlq-8rZ z@(J%c#~|_Z%T6PcdeXhT*wemId5NZosc=&6Zx z78d#iZu#jS>v(@f!X&IwL?&OFbb%%pXZZv>fprC+EM>X0vM-PK-E&;?wQ;T(OA-UZ z`B$1hjyyo>2I~i-#Hx1V{ELV!v6hg&e6rG5(eL<1ig{Y1AJ1S|=Re zBJt0$zjtQZo1>Q);z_g~0M?KG>7YJ<8kE$g&X#fzWoKcX<)$2(v0^uC{!PmeRqVqw zVR?}E3DKVXJ})bV1<_PRJA?&CC;X?~6(KKyWJh$#-{v3;-MMtkHF<@O87F zmsRz3W`Yj9QHOGzfD#P1Gm+fELIzsOMpai^a&P280C zSjVC2Gv9FHZ=ES+W6g1XwTb#d)lxR9;8$6P1#wG_86WI4uio{M1v`dLL3Qbox?guP z*hf}c($}%dX;y~5VmCagJ=NwOvNo;^2SZNkl_5ra-YKFqST7T^@9ipf#mk5&9sKKU zU-hr;+5kx7U$w*T{&DjQf@QF!UeLwRq1Mk9lfA0W>-HtpG5`1~3b#n7F0aoCG*=E1 zr90of{=R2H^8D?bFG-q~ujn!Md>86fy*Yb%ta)z!BLyK^YOa-U9;>=>aAs97qsm!# zL4cRQ)0ytxEJ?q`e*rjUMX3=OBp+TNnxS6ebboG^NuAQ8-=e1_cK34kPFLk#U02Yt zvbZ@-=lQ&_ytq#O<3@LJX<>Ptl7dkv%gILbQph8G4`mly?-IJLRjqB*e-_PCk8Uadz_wGmCL?me4tW@D&Usl!xdTV!ajU$1mkMhuzyRE{sRR~zjEQf;%rFK&v@0(Ai8DV@QzET-qO z9P=_p%gO7xUhYo=F8kF1mFGHd?gJ0>+jumexG#ZjT-_yz#RC!W# zi{K*3PW4AHKGB%8r!q%xGe@>*r6aAh_tm72(YFFn$z#o1%1}qhNnc)-sM9D)p5zXl+ zYtFDN%GUfYk5yM=MQ%E`)_{Fz+T?XE0}3&W91%#o1J?ae_k zD)l)eA?*dHRNKLrWFd#%`H4CUA=X!5Qm3IR`F0DXJZGs=%!H~fZ}guK9ZCib>+4_x z?&@K*JB5i@HPt<|A$;ICMn?NSYVd7#(I@D(yQ;lgMimSKK8i)d`b8nNX0Xbr+5j8b zz*VMsj-+jCh;st#KHor0sOTL`9!G&+@lZmeI;I_LovlSic2d3_s_Z(IRdu%2G_;J# zDAH!4JpI&DHCE--xeG+Xk$|MEus3DfP*r7CgDfRAmE)9&lclBfnLPTXWf?Wri^{4A z;=0N|&0g|aYEx{MG<+H=uvQf?;v}l;inMbOsg;!z<_i;MX)EdKB8?_iYOZR7K4q#( zONycw)dA8nC4Z9uVU_dTOXN?=TRDKg>uX1|KG>={Drzc9h&3rQbaj@J!Ro{cH1;7O zY@LlIf3w)tSRf1P%x_bdm#6CLveegz)RxuLQ$>##odT*&tXw-b(TLQv^_1Bcs8dt_ z(PgSn=T(MkpKCFL|LsUUqJv$h4g8)Dt6cvv*t4{i6Xhq%4KVc`JS}#vGE3(zO#)P| zr4XS%wJ7qFQ}QWI(gdDZO&n=cRg^mNgNRvpO<&$KYfenaR2J5*JsN!v9Jm)quhAt; z7pjZKednfLPJ6ikFt5jEUQcFjCvVJ}dWFB=H>Wo3JG|RLH$^u=5AxpY)2}JQ1#dJstjF-|_*@J5n=ya$9x1MPQ4nVz-GF!P z1jz_>&Sidc{o1K-Bf5N=f5}JAk|JS8A9#&6+!7Jg`{vCC-Px*Z4@Cm&>PC+pClN`^=13T8sT579UNg* zV4uDe$+~)^hkC7Y;<1ew&bei5cF*HBL(H*$QKBtcrwym||`$D^z5=@_gb#q*6Fxs}fNP z@?m0lhjeT$Z7+Zk!O(}masJ>(4K2T~-YLuRjoFv}@s2cS`)uyNozV}<@_5hV@M`T2 zD{?m$!R$A&e~iY(B9QbrUxOJpliB(2A-?>gq5!oRnAct7dIa ziFm^FMG1@Snd57?Fw75FHUo6-fVN}m1|)7-UwQAj4f;9nZ9l?kYR>VN^s8%#mjuWyLUjv8EYLV$HK5y|Aw!J^tOvpe+DI6+hc%gX=umNo)E$L3 z>Ga9BDG1Qwi39uT2jhmMz0Ut6L2Ns;#3{@eFbi}W&b+!Th#f5Of4u&raUCptVoMVF zojnx#UCLA;ucw9@62+9(wQt>cQh6-#A1rh_kCxR{NPt9BKkXnf6Z0RYhkXH-tm&qJ z&9wp%HMecMP|RreqV8Nb-RNZ=`c(S+IPaV)HbhgHlLtG|>8`g+LMRV!3CO?mYNS1+5pJe52BxH6 zVFr01N|gDv`;HY=VeLhDXVEPX|Dtb&J{J=|f_Y(d2W^Y^gXIF?0l=tq`#^YM#ttmg z1EC%?d2tu1IOtH%YP5UqP98fo(&dV~Uj*bI>Ij62M1H&C+?)35BBk4(GZTA(Eaa+8 zyi6qFkmynPQA#4APT@Zd^$GrR#q)>(Es2zqxDPD^Lhb4h7y?mv>DMT{FSoxa1j&QN zcI12{;fUtcdp&oXueWn|c%LqyG8fq9e{usa4wZ3JZ<)D$C^Vp7sP|NyUcIYOT4@S@ z^5f7e`Emb*z_`rVeY|;isrR3DP?1*~o4Pm%VwhNSe43$M&OtOo9hc-vXL68t=^^B6 zc-?u1;xNi8iGWXIUZgm*;q{cSvH}SyPys|*d_19h$eZ3v+_ajKTr}* zz3*&a34D+UhevN8UfDl5zL5OmdWQv(^G+jEeu2e8vr6m!Yla03;E1 z)X1j1r9W>jQg5-jKZ;rG1QbW+!lTFlihJ#df{BBI)H#oo;PLScNrUmMDYx|8AHNdG z@@d}kX%3EOV0`k)X$wgj1T^jyXdUv^Z$Cr|XVNEL0r$wJCg$B%Y=hMa`jyoBeK ze+Ag#EALxnXZ9my-Km1OkyvC6c={zF;iLS)fpz}RaA^`e&z81ce>rIJfm&7IRwcB` z6+4==puEtAD@bVFC2=fu<6(sHP@>|8^Bn?;u@ReBw z@ByE%F0b2nXJb)!i%p|H!&wOe>|dT04n8~(;{1_?A2ClKXg#S0{e&9WYIaM}R^46= z$9VOsh%ngjGP`V>Iov_Y%wA?cw2Os`$L-Tm(1 z{!lM*>o~UQYjz>Gl6`XtuL*S`gwQ%T3}Kuh_C@Z4FJXiTTsTT-cXB4yeRE@&q=Jzs z#g)Z?pCa-tbXT@>TwZ(5b>l1aXZd%EccpjS=eiOgYLTe)oO!egdF=wm6rR$W`A-La zbbRD3EMJ933Fb&Vi6qypgG?q{WyvnXDv+BQ0ro))6Y9g`>!>kKH(`at@^_rvLpF&MVrb-(R=_5A?;!udw`5C0DPj`JsrFf1a$DIy0!jznOq z4a|rLU6tgr(C1B*WINsQNcyEL1Y4HGmL$(2jC+#6m!tqANt_9m<;jvM5*3-9l2B2C z_UOW0L9`%AUO}=j4yP<x!#_4Y!<<8$!)-$<``-KS`-H=YLx{tIk%W=?QT`Z$m#~dI1cI=1bsbL9QHIWC;|HC21r#QXefZL|+I4 zy`rzhFGT&@$-ltN@AJU4jGtNuZF2J(L==n)&Fg2IR8OH99zW8+cYp5T-H>AGZF>Li zu2G!3DTjR`7=hYAH-~o_fZeIvt9G}ScHM$IAvRkQ-0|D_74su=?L}rB3(MMvL~6lm z!8TPLN_QvvQiH32+K@I$9OQQGLdptvx){&F97wqEx%j!V%)!h-%puHyF@YFn-$|V6 zpGWF>#^h5A%Np3FAjKq{P4V3QH7DhlYP1Z^D!VBh?@i~s81X4 zsE`0ymD{MmE)NIGU6wvMkQbSD`C#v+zNO%7_*&piJclnfO}I1i-LxQUh!tQv@b=VQ zp8i}!JJNR8KH%;G^RWLvE&)G=&Uu;OwFH|sef0UcNycFW|5+QVf6=L>c%Q`M5TVyL zB7b0b-jk>TAahiFTngC(h{_41+!5)&Kq+s4W!eGHxB>a+4t(4R^q?2;#?a@0Y1j_S zvh8?moj5k+Ui*oNq*c`*BKI5PM>Kv!SQQtty zACMpXUf9j&CbN~v!d3DVM%&p^Alh`>`?+-65Z87Nh@OIbNMxIN{%yC}_9A_PSg3{n;n&gxhw5{zOc`UiFb!rAbmHj4`3pa>br1GU;RE9wqH*^COnHCLp3 zT+S6LgxL@9pc^{qigoEq`Xxyz?AJ)><1SLCjCB)y3&jDM?7?5Vmr-)9ilPD8(gphez}^M=gKh7B;jY5N+ds+>lr2zf#6$bY}z|m2LoU? zEa~njvV{CZAOo_#7l^(Wn*Rn9y1pBxJ{M?JAN)TUio;#II#Bj?gI|-ixAk{}UZXAE zF@)R%dK2#Zhwumly;AN9fWKh3&-D8OU(4Ls0)@l%KYpu2HCNkjtDZ`qy%oNgO5@+n zjE!&!qvZ?34(*8U2u$vBPZ)ZjUeC7*mZXHfrjLa+0%ez)z%(YJ++3BQjHsu+n6iKl zSk*=Udz7bS-`*IbgE|puM>>2&ak$!557Ggzg|&LmT>)qMWPIL+6sWBFM<{Tz&FfAv zXo6x!kvvY8DXUz9tTKegEGA1TJ`@olxqwD0ry@=IN0NFz#Q+02QLKb|7>&8nX2_<5 zjl_&P+D4jM?ANc7KoS{bYSTm#l14fi zlNnE66>yC%N~){cS+ueTmG4~y`)#qbFPB@v_rf? zD?6xoWZeD*7sNptkpi+#`lQ?vXWz_0E6`uR9D;Ud$PUQ#6kL1A8k-AqZMBJ)3Q2lz>={HZB@h#Qx_3}$j#qKwBEBkMc?wJyQ=6?U*ua?$lebAa80}@_taTWGDi%@}k$|GXPTm3Y3{*J5JE(7-e0no}+S) z!OAs-%nqg90s)Cklf;*jXYs67orlhGXd3goZ=NBZ^yw4l(_Zz1YG+QCq zDj??sa<{{NXJ23X$n^)0l`Zvdk4GAprIxvrD#ihr&d^AoxDy`~KH10O z_&XopG(KrhWgR-kL$S`94~MGH_SF2c3f*>iublf|?6I|w2Q^G5XO=I>j@hYYq+<#K zv5oo#O3Sc^!gYhe>Pj>d-ZYZIW|O^yOX7-^#bc}~rUsO;S(4c+wed4D3S_7`df(WFzI_@>3s38fgUSQF%)L$q0pw+azI|SrHU4Ak7d~6urfwyNA;rwyPn&( zPY6R--)QQb{3;oyEKIcm)~s>@e3OG5(Ra4M{N0vNlwwNU1-^fga`8pVYtC-=$!$K& zGv~B?{mYSRh|^CB#-3KDXWM&1Iz>P^ML=Fd(Du64HDTJ?2kkVJL-jca8KwskVejjzW zE9R|8Z;n~xJKobj*TZ++6xYu9QQWCY_s+TB3S+?!%&U#O-}$BfuY~7`OY7F{UcfFp zYg?^`wOajwe7;!xKtp_!tmS~$S$YbW4LuaPmw0N|p6>#Tq!$h-f#;KQ{ zB3nscdGyT!z#|aloA``#Fyd1Q#5O5V@!Fg;qFl2%?@$Iwb+!=AvfW- zh2=a@^jIHt(PGj(X=bmXpSvdM8%e3(tvz=#(|YG>kF9t&NNUJaj6SX7r<*JP`#GX7 zP9Z&NvAL0$b8;ZK0S}*;!WmCuNJ+nIy^lLkW5*P%PSO?`d3NfPGDfPo98f1^w1j@l*Otm9MClzCly=Ehmo zq-BQskwkE0Pp)3QrlR|b-NXJvYMmATr1nfo8zxVY zizlDRH}8kOg}%0qR=-Gr2g!WTk{P~vbAmJ0_{U5Mz&}#oHSoZ#2`%Pck zoi^_m`ahIn>Qt9DuPCp7fX9(tP;axAOnDoI>b-jVdOgF<36kp5p83Mhi2d5JQ>e@% ze#M@vS!p$+2TAv35mP6PR3mFR&w6C>@=BDvTw2j^vvdAVDX?~Znz=qX{T9DZ!5lNr zX>e7(&wWVe<8}D!PqlqwN8%A?XnT@;oxk8ps%LQOqkOuh?%^%HE}Z0+P=Zfp87_I= zIs$;}pMlcml~5dmfLSxF@#I)t=EL2$&bzj=&ZB8<)S6KfN#-(-bWZ!vW%*Bywvom+ zUw2KBz7k7a=2Q`Z2gXxlQ$qggrq+~CUjKynhUsye?V@0t-qPQ-gXQYIL)Q#;KEF(@ z!G-Q=96REhakBs7()8&verv>dYxKAOkfsgely~QdvA<9BL4z|^Jo$4DaY87qVU^yY zqYWavw*3W%_Kl2Zc>$lQA_0xZ@PxLb7ezdz8YVOvu?ufv~$xmZIp&_qz*emNdJMr@N^k?^wPu6daCr!#%8ne<%Qm?i|-$q+J zEFFK36uO4eQJfH8yjKe!ys>+`4wKq1*-==sC(lX9bS;5bzUCg6p6=@Ox93Lv5|fB=kdr;Bg%Pw}ecN|L^7 z?Ah6=tKFZ`QKlKB+n$eck@OZGe{^za`$Fgq9SfXokstG`IG(+}Y;$an2RdB)yeH>o zF708+ySOc+F12UqE;Wu3*E;JYtn>nMtRg)7fi0PN`(lC@W08p8cjU+zsr$IU>t8uO zMU3k9iAAGJR(8tZz5zVl?~ zRxSk7+fK6|7w&xlTA$x0nO|XFVR&`!Sn_XTq;pslgiP0L0<`%oJ%%yKawA(KO^s_l z%Qg#m#}CIG^m8Av9WpZP?%CljE)TuABJTmL{h8Ii6KC$nmif+pV_HSNYigH(l4rRU z$rbY!`;IS0VT&E9(`<*bXVrZF>xY?j498xiouZFK~}%Q zh0}AHg$hbJgza|CevlC8zsi9i2F4TpLLxqe#IA0ODxOFG0tO3)3QLGF!mxkU2}lGh zh91#k(q9`l5O$(ORvis|i{gyDRv;}0F8^+7HhARUcnMN?==q1 zhC*;jDBV7G@)Um?e;@yp{zmyq37|yvuk;V=O&f^0ow=KNS$VsF=6uENw|s7U)%m>m z#QoIy*!b*>Xi9frkm3?*fOtoIkAG7D%qUhr03$Pj<^?b!k$eGvM+lM4GBOb{jrB4! zks;j?67is5VPFwpVPoNAVPX-M-{O<=MLtq?f>(krfUx%@^;P%j8mJix8z$Q0&&l|x zeFX-Z=0w|hh`~XsvxFZa!dE}9KrAcwF!Tq)2g(I18d@8M`#0K47x34|-wN;i>r3o& zoPSW=rITuhd**~-D6xn^W{H!!cl6I>6dX?7e@guW=5wJuKs}f8MeP}QK>gO!l@gRP zkg}hGvNM;$OkJ9?08V|R`e!?3BBe5gntDOS?9abH2@lE`f9RfDO6laZ zgHHpe1fztYd{+mL11p0tFTsOf2ADydyha5-1iO9}_5%z>>zP;aY@k~K&cPSL_F!8g zMy(~)uy$oG?%?WR!TsI+p#6jW#(_rt$^EP)p2D|$Z=OJ|Wn^k4Srr7|yQ&7aCRSAJ zBKnSG4#e%`Ee7WShD@%C=Scq6=k<$wDaG9V+_LX!C|f^2>W`9*=IEgYGUQybXNz+c z2Ok|32P(Af@}=@5e|bzD>$&TRUA3*?e?()xLxjA~t5f!cuYdj^DeIKY8^#!je zQ=a*w4`oT5UPpf@M=h6&3*!)1zKQt`fQ0-L2Wtl%DmsUi+sL%IqakA2Fkq0zlVo|% zQzYss8-AaOAYX!41A`965CS7QOa^&`Li3m!CM{wtQd*%S8hQv?ZPcsS;s2|gMOCDw zf_rCU2_OCq_Igx(rogt5 z2)U$L+BLIzY@}1UX*oPe-Tbb?hFaPQCdW-v3oQQ!Xh4_00wI+Pe76szH@04(k2Fm z5Iq_6RM1`g8ELgtD`yA#rH%5Hrp@uKOPilo=g;+3rq%m)`Ui(x{<5@&z8BIO10(%8 zY0YUZ+zX_&rmaug=%1LjC2f1!F8|oHy@6?ITYM#H2ht8xD+D@lgxlV{xFY3fcv9M#aCMkHzbRZp zZR{y-$HKG1b17eqXw$;s1;J)ci`&oeqTrUa3FJBD;U&~J6$A%{m-&{5mxNdN3c@SH ztEnZN7+#yQCcGiM$=5%;HN1nYlowu`I@ebkZuhMU?+YKK-q1xM%P3z5!bgI0!pFfm zmD)}r&xS9gYw0fb-t>2)##oR+tO{xhFq);X4 zW7130`}@e>g3amW{;BDezS8t6s^jz0r});TPfwpoo*7P`O+B(Jy_o7qN7|P3dDKf% zuj=1Rz14v9g}!3Hk-j)}Sm;pdiu9%F%gO83P`^3BSDD_#bo#3F!f-puU*kKJzRtHf zy)Aun;8a?D`Zj8FqUk%+_XLKe?@vEO^igUts3r6_vYzQD(od(Kqx?=y@5nGR+!@Ik z!HkTIoQ&Lzff<8+r!$6T6lILa7?m+Lqs%uiV?xHnjL8{OGiGGeX3WW$pHY`lpSCNb z!C#iqn9-cklF^zH%~trzS4}tp)u?~ z8OKtZGERoZaQS8&3!ce1lX2ePm?=Vq{^m?G)064T3}qx|Mlv!o`(+Nu7??RIa|ro^ z)rN%kWey8AbB~ZYJauGvZps>JQK`30Ysnnx)7Y1SBkXkQ$js3xYp5SN&g~V`nd6wI ze(F$QUub4{TFSP}@rn*?N;{L%!M^9O&8+a}DnA6xaVL=+ zpR4Z$N@cV!nz=S}Ltt3uCaxVmjkKlO;h#wStz2{2dRFZ?ODXQiZ1=TMyR;&(I=wvP z3ReUonfo#iW*+fR%{=IH(Fm$6^EgR66|AEkZA);M&zo_~UrT;VKA(9On9>c_MYM=3 zFp{vBMnS_O-t=<9RDC1SNPntpYeR*RyhuJ{s(rbU0{_%VVOnm4#(V5b)S@CS#*q>l ztqu0KM#j+C$HJk-K3AkvVSjyaV5FR=%Jj-KccdyZ1$a8}Os+4XL#%)3kn*%h9!btk z2}fr8Rz>DT7IOQ{o|%3kvN*Cd)a1*HEG4g=9$B7dM4BS2fXf;C>mq9+>q6_eH|Ly& zTxr2TBv=+{^UwDcMmBS8ifoHeNv1dX8zXxn`@_|dLy@Ba58?g6W08ES*MlP`G7s|j zkmII=(}I!Hk#m6|!CmP@QyV)qEy#5(SkE}pk-jJ7$}%E(z=zVq)HfAy$&#L>p>>hG zU|E)1jmiR%tmL#{<_6*fvof-Bs1%O*4`k(%R^iBg=BJGc)@2RM8XP#1R!gl!lkY@` z%AI=2vb5T)p;<+QM`Vp6JT|K=YeH5|djG76S(8JDB73r?X3Zd6n>EK*nKhs4OJP=> zZzr`*#lfPi`t;(g2JR!W8ma7ZvYNA6X!O2^T8X73d92Ty)|l1mTbi|=+8J-wMjnA@ zZOPhBy`B~-AWj31=d*T!-V6Fb;1tu`V`d!=93k4xd>Sb=5^ZqHmUS#`zJF|1Gt;Ra zMW>GBIY`z?9_Z5mZ ze@gx1zO>rt0;&&V!x5&l&NH31m+8!{OlP?>wnrCHEgt6Im0p=r+(oC&AREq&E@4j& z?IfL-MOQ?cqANjrK(9`%j;{5Uq!;*yMmLb&YqJi}+-hiaQ*>)|ZFEPpJv|)VVA1=S zCi!cl2NC{A>e}dW-=65HU`zC@Z*lZ^^n!0nwwCR((O#yr158KCsmENK9Zp{w=ocwr znryz5M^IgKc9h2_G*UehEKA={{aS5!WOn~xS$1C9K%NPvO~}p<3<>S`oz5;upPrqc zU6|J5Ta!9By&$_d<3P$frc;kFO?-?KvWo+&slEwsp7Lm6M_U{VL=e9Z!%3hO|6B=XTb=hs%o3poN@66tly+5TS`%q|l_R;JU*{8G5 zWp}9AagGtF$#L@>Gbfqnk2%4djGUanIBRaJX0>WQYoDvCSt{nIIk`ClyU$Y9JTzx; z&d{8qoDn&ra>nMA(xYT&T8@=g2v`cy>$}`QkY{Nyf~YTO`CCOEWL( zJ974N-;r~GIXQ>9_T_BPIp!albJA*Q2Sd*@Ip=Q?kv0DFx0ph9{LaxKhU zeF*=V@x{@d^M&ku)>sAnYaxX&BSh~s>cROr@HB8%0Z##5KzJYUv-%e?=f^{BK=K;v60Nx3_ zMM$mL3<#+oW2`ObJZcX!ZHxlvVQ@}@a~L6m;4EO;5kQy);QR<-)_~K-9Ag&3M}hA^ zn3r+)Wu|2g)ACW^9NhgILXHJz2RJjp*^XFc%rU(x4CJfuQG~2#tUJN+D4mrA#ts2( zZ-bnb2)T)AXEnnA79m&S?vFr!-?Bp{rSd)K^B13}Pif|1j?MzxZDqhWX-8xI017@VZw)*C0%P&<7Bv7WAvg%V3t{IFA%3 zA$1q%gN$VdW1|mn0aCn^rO9asla4U!L4OVOacJvDDw`eu!}@@;8DajcWFnQHK-wRm z!!hs=LGliS{44INvN-1~hDQ~_(~8&!IHc}$R>Nm%*h3KJZwM1*n!KwBWyhQq4tObJ zEzumudAzuvEzBHQrOFPp0s2maJPFRN(4h^Sq43gvpa-G!4>E1^0VkJ3I+g$54$rAk zKE`=5o1jBEqzy&xs!@*9l_#@4T0Q58@>s(uTDh)4UI;t?L)9!;Y%EJ~rovY1RH-SC zLRp`L1@3}nG*~GNI?6QJlkqO)=S<5*;4I)UG7sf75B!it7sL08xHJ*6KZg|Ot=ggN zPkhHj#Ybr_LLO1q!~s=Dpvg|8at4<98tnEk(mJCo1DUs}T*G3UR4qV>e+8-3@LeOU zN)tYF1{SMV?Fm~_9N7QO{-;} zM4SIs+JjHU@QG+sGG~Iw!M?<5p4Jo+;y7YA?xi> z?I&dBsy-3ncOhi1vMoI4_mK8Y;KgbL0)Myza?n;d=BU)cpNrD@SEMx;7W*CQX`UJz zAmlkn3xjr{6&i_HL%>NyS(LFf$3pN|s}>1*J_0-uoL1O#f%!?2T%>9iywQ)gt5)@= z@U9nd_XUiL?tyMoRqKJfvqAqm($x{`PJ|x~Igi56zYfWJl@BoARmi2_9?zxV8jRi$ zzL&5Vev2N*EMbldqYiT%b6gs7R0I#a7xXmP@GfZb6IE*+p6CZ8-RZiWY@BS-tu!hZ_6s8ltmgJux&X;sQhYrA0c z|AEYWXi|#1m?ddCO8h&jZ-unm(b^n_Kl}^$ukg%8zQmZux;%?{6%sNq?>GXB3LZYQ@1pglR;W*s=6zo=nv_`5@L+;QQn_k3iLs=gH{U9WisPQxMbrCxJ z3Gz>=(JXKxI7<{?(eRCbF_k6J?}Ppja=iwzzK>j-RipRLevFaIP-wN(vIg6&*fED& zCxrZfYRA;b8Fl1a(D_OD23lR?Z!E!CZSnh|Z9(f~hS=ut@Kp#Y;h9gWJ_S}fp}bvL z5@X#AHTr^XpH_7r{_q9(z^BnZ&jua}yc~B|g3bW02W~~&UjyqHITS+jF5LYv+U*?G zFN0GF{0D?D2EP@YhtWq@qnF8JTKo?E#h=i2Rl|CBL;l?;Wz-`x0i}V76T3~p{e?zx6=W@dTp~j-nc@Xduh;<0D zPN+5?J$03uCnD~rA^(2Pg&YFT4CH;kvKYMRIpytq*U_kEdCE%2#X9A;NUH<52^<&b zhoH$;q(04>HP^Bn^t`X2kNz|0H$ZPffBGPY7e7V16X5?f&<8D&J|Fgkh4uNcwzBQr zxSNC7L^DPy%h?Yd_hS?tWSaU)8uuF892?k59px+&A!p$3am4iik2MP@*6$tM+Mw3x zm_KSx2cP(tWN_{diUD1jgc3%r7Q^{uB5ez^?$mq-NP20|*OfF2ETG zndOi<5OFu6_3#KjrE`qnQ+o~G3Fdgr!RJ|qwLGmr8>-!jGsi}1pVjV)pDA{)R;k^u zRcQ}tU&Fa!k7+ZtdHQ<8i8H=O5{xDoM=)N5@ePCVqC!j()nXb^HDZ>SD;9`FVu@HL zR?u~&SS{9y4PuknDt3r=u}{cOzv(4hz*qH(xqhhFdOngHO$L_b!h&A;4toS+oMvIr}_c?K#eq#(LS@HA6J;oQs zSmR5^m&9Ghea3yF)VSZ6DDE~U8I#04Mzv8b#v4tvhUEVs#>au4DCh{L}_aq6f%Vf~uozth(5 zob1pH&8;PCZrP*-wG261%h7VRf!bj5xgu=@{c^QY6k@DaMt+c{o#ZlhtjDbFDZ*QT zml;dBj|2a9;J@+8r_sT*tYWN>2LA`(f`@1?U2C2Sb|=%1wGLcj1XFo3d{K+ z!nlF`jGZNr_8M>)@(1fHST~(&mi{uI%ul15?L1mDZed^HyJ8!kDXI@)Ec@g1{wERZ z6Z$jUV}hQ^cgfo_c_l=zqmT{8hj|>1xKYNWA%{5$jt6)J;%-7J=Z##1&*gCi_;VSX z<3N9f`4om@m4h=D()KHwu~x?VXlf>MZ}ajA^W%E7V!fnm8u;1Xriy zMaLG$FI*40s$5@Aa1cNH9o`4}J<*B%p7&zE=Y9HF{S8@^bH+2|nLvUMyTJUW`8DIUkqJe2&Y&vSKvL zw&+BWXfp79TsfUsxL6oZq7U(7@tEfH_-a97$kV_O(<3qBX-@3t*~r(P@_N^O=)(8eK9*hM_v|p-v%MQ|yclqvEQsj_`*FV61|uoW zm~E`I?PbU^;(4sM?pbz(%$Pl5Wgd&iX^u`D7cb+OY`%_HI)JW7oP=w2;D5y+|+4Lt;(htV`lsd5d4?CN4-^l(-~uS>g)2p55}&Q`b;;JH+j=-liMn zCuTqB!+Eo=C=c8JxX;FHY1t3FSQ)HLTy0Myer>!={$u{y*ggMoo!EakZmi5%ueBDh z=QS2)L*k~yt%*Al+Y|RC9!xxPS-bRdMfqOE{WlADP3iQs^ZM@Pb)_rh#Qq<*@_o6h zWtX0|PCTXT^k&ysxyS8nzuNL)_x)}@z`V1G7rdI+#dPc%ZGgU8@i1XkFTGK3 z|5$tN&GY7a3%rFfo5l5_|K4J6iFb^*H10nfzT8{stsZ?xf_TV7x5f%=dq3|*GFUwZ3G<&wk@iyQX@=Ql zAZdHjE(X>&X>Zbjq{B(al1?U_Njje_?0spnne0jSCC#z-J;|Zu2(JB-2iW`cZa{vJ zJScex=NEY-NFJ6v9PvgbkG9(@d0g`Nkh~~)N%FGf70D}; zS0}Gc-jKX0d28~HR{t!zuE_^gdB?6Xc(Z(7wym}Mvi)k+n|OcM^M9zj-m(1Hukn7r zN1N2`dO3X~-R*fvdRAX~Nx$0jI?~qO*{@ca#M|ow%HFa1m3$=mc=D;_v&k3wYFrok zy83znfxh9s(Z2oTWAK=)m|TLsd42Qab(qtz)z4VHi;s(~_^~ifPMt@9SFY);qPMF`8epUOiRcYkgp+(L>Nb@YbmGB*t~`zlJcs z0)0g353OcFhdU8+FYbN?IlTw`JjC*W|84MRA=W+6b07F85az?68xUp$;@$$8(~#fS zad$d&djq;%fDQ==IT!p>;GctpUw~f;`ZVarkP23sjUA9S33r3I+Xr`_1OFh>O#$tO z0i|Z3)LbZKLzObtlJ!i)twY>jLR-)}XqP~F0lQJ+F67!!x&A9s zuK|tcJ<$7%cutd|eC?tLb-#_$~d0ikC#Y_^8-JzmLgZ z$?c+0pQt}5iuf&JQH*uerD7z(a?wPvN~{s zNSJ=~Pu4NW1`2D)fz-}t8Tn)rCx81?KjxT%^+?9knM3qitc*^YBmIth1@F-d^D|^fxC3|+@JK}??h2-joh;cZeSXfsrO#gs z%c0WmFLK2Ik%w=;d`RSr4^!EHL=;fDj-WCv5u@;Z>CcO?;x17t?k4YfH+KeFX1}+ z9iuWk%zd-AymKd>O#PMDTA8CqnUHef(^T z(iOPN-&Jcz`2LX4AF+-DKZ-CN*fDz^wE9Nvi@?g7oCCJz52!9hZ}zoZ?3Ny?r8JZv5_Pnu`U^G@M3iPP%zn1`J{XUII`j5zyIIzycUC>564Koa6V=OB`!ev_>q zNs0dsTfbW85OcnBm^sQhoMjO`(mC20p;$vnD#O@#{Q3Ab;5V7lI7U~}o3t3^97mc( z%sS_I%I$V@va^EHA3?bpfgBB`bf~u?du*Yw)0{OVwG|R#Fp;!lm`}RZQ$86;x^tFu zt~r(R+(5cAaB2)=oeL-|$&IJN5@WzR5RhfIlhj=-*|~@$PFCPt;#@|yKVZ&qu5hk& zt|m*arRxUgCZrD?oLiAQd)frf9nN;MnXFH_b?yTd1I|CkF^11@9uq#S*e{+H>&pC{&Fqu4?{M422DljLy3eEXcJ4#$*oa0@k6r2>Qk32%S7P=O@mb#X^np~?~Yh3GGO=hdB&9ymJlAMBT z8>P%J-MQL4pknYn*G|_SN_R@!qA|-&cI^km%QWs+$_nILu0yV)>>;ibUNbUB-1rWnr%0W5;DxjgdEqDgj|wt{SpQy3{DuD zP?Ru&_@l^z;}XUulqF0^m`K0L2~$b(-h>$>dv-#tQzXnun4eIWP*2>$3DhP~9I7`7 z&CYhR*J@WD#c468x++PlM%TQAR=PhmVLi#*==3FQN!XsSD`9WKfrP{6#Drt!goKk+ zdMqn!9_AkIG~FYq1uBEx zRqbiut9vxn&l&DQsZ=lia2`!CmbPxvSmN+%?YC?pf5%os0wOwsWO> zF6po!Ve%y&T;~)3l#hE6gL{d48P`A3dj;xYj3zH~uXL|=uXS(Wdh6cg_PMvZcevZ# z`{Ewn==8Y{x{s)uNjYnw^ch-}-?@)dzE6=ZCXE?G#H%GArnc67)_uW!wvX1w)yL}` z<~j}gHIhF~b=J5}Cp1&*yu~c*6L1Ac*S)GOrrNQ+PqMIZ?gp!(!J&DE8`HgTuY(wg`ohEi& znQSqn+Xj3GaJ?0mH5mj=hAKbo;IVft_*;Qz!_JL}Rg7FuMCxvo$UazREMrGN>8w(R z4Ur47`BanwTd4}EoPx|TYTp?+V=Nm^5EC~(URK0*!Z3w>?wi>5=5_Xt| ze62vN9h?`V02;PfC9(dZ@*%B~G4UCXP`jSNp8@*}1KozZbD+a!;N##d#NGbj zgh1C@^;hYz0@9{Jb5&9tvWl@W5V}2#oJOHr4e%+N{g7p#|4x+b6xGhi1DN}B`Kp-; zYQ1(2dQO4-BPem?LX5Y{m+ia`I^?OE2rPg-B1p4`Lo_$m$d`*;pCE~UKRZKc&g7{T z(lZA;M5L!q4}0c#<^$?1V7i{5!TR4w&}{Kr2wE+EgT-s;5idrsx2`dMiv`U#y3s~& zw4m9#w%YE+V2gFX(I%_Tx;ESNoMY3Y&Zc9_WuUniUSkhvxk})?Znx6hWy|{tJ`QBceZSD7BI`H*? zbvYS+Edx*qE#etnNTo+LO!aK4A0_PorwOnw?JbM2XCqm{SIU7LH@ zzu9IFOGcu&LR!7$zwR=+#IEaYUnMHMgN|4fzyf*^t0%Gws1CIP8?thGf2r#9O8+$*NMXjhTCjr z^Nqxj1f#pBoj8tQyh^)U8^yj-;Zc4WtBck(agwD=wWoVMtheq@BdGDj>p zELcRa#NsVe|B*JgiMx6!!&n}A^6PDRv!xL)^A!XuJ>A>DH}kcg?d_G?tKQlrikG;W zV6DxEHdsF9vFUudc4&^L_r9q4Qhwqlf~^EQ2-*qudAjvei3bUe5FGCzYh&+_c*>*t z+!(~ph?z5NODJl)!9Z@zUc@O1Cf?LfBj z7OMOrj|ARgD_%)=dwItYloFH^R1#DXOd*&~Fq2?5!90S6SK%M_KBeUmKJ&3;?1aJ^C+YQNgY2fbadrF`siFVkoCntpHB z7F+suyIbI0Ot6$-xvC4^CJR;(tnqY>Rbz2ua@mj9dAjOvqo@1WI9B&!<6k(S21=z&R8P629671oSBdVS}S6#-wyg^ z@DGB6xW=Er`4wc&0%tGid%*Vr?*slY_#+Tz8u06o^9E8(0DTH!egS+MvF=959pD7P zc@CTug#QL(H#AQ`E?g>iLvgnbl7U?+-M<2t<1Vyyj0au;&RpR22=ixP=x;m-tO5T8 zcnNGl5bQE-ro_66XFfj?QKVakyDQ_%S@A z1I^}>FFj4%QRA2|3`d=#PNX>2I9f%}@vP%NM4IF0j-QJR$8(P7MJCP>kK!Eh+g$g$ z?h_w!O>#{VgAlfrLq7NJ3Qte{24oNaGG69gEWw3q2Tda&%V=H; z0!lwEY+a)S{Vm87cgRuQb{@>YZ=&tT`w$exyAkSTgLS`-piwr<7W%aU^Hovnt$#DH zdqKtBNO8B&ul4dLT3z3>tkjuRZ|#(+t2tll(lezx4m+1nzK%GDa=v&MN(c7R2%P03 z1fMcO^k0B^FG0Tf95C*hdx56`H!*g83wS87i&EKux95eK1NuDZhk$EQ2g?XI$r7p` z9-QAV919$eQ%&Xb`yDvH-;MM86CEv%pNf!Uy<6|5EXR3+LWx#7h*EwnfMc2t{yi>zr z@uvfS9xJGS7G0;QZ2^W%HsQt3U@cu-JQsUfL9|b3)IK>xx#%ZG{sZ1Y(1b(F<0mRw zzLrn)VC^1ZYV)=EBG0|qeM;O$C6i1gGyEUcid)Q(ACR|^EB{sgpU~wST9RRGF7IE&&o8JCPvG2nJzvj zGi9b2BeP_d_`J-P*TVQY zkMAuVEO?YFwWWlUv>$6f(SEA6YR_t0U3a>E=i2A`FV}w8E3N~s-@6WC#M56S5%d>% zBA?2*P!x+2F-DZqNT-r;m6#G67fGE*Qyt&Ln5vWa6?BTuQsTRrraqWQ)W5=b={XJ~ zzmJgL0mjY{@ih1s!SRrH8I-~z?yt1RwWp|u|DN_ek>EPyIxO4?$qC6Kk?uLQM(sJe zw^`dRBHC}XKZ;xR=k@2saLUh};uDmogQB?4f_D7>$Q#A7Hzw>OWUg*&<<3R(*%w zuJ6+i>PPhB`YHXaeu1wB_1YZY@EQRlY($OzMxK#x6c~j@u~A}-F-o;&qui)8s`O#T z6l1zkWy~~Y8}p2X#$scsvD|1fR`tBb^^09C{bJX6_?W!QT(8z{#u{Ut(PnHmwi!Dq zKYL>Kit&{m#{Qo6irc9d`*gF*RoWv~J~4gm=|RpRxATvf59B>_lz$nJ78D7xCde00LC6dc?S4lU}z#{*>*s| zR^ku9YFFX^L)?D`{cGR?;Ln2pMWp@%r2b<_P&=>w8yME;JPrM^-%LWX{0TVN*{H!* z^4p-XV^Avnu_Mvi;r0UXec)s7qQp)i>hG9?y8^q9)DA;|w+%!pQlE;DYTw!`;9w74 z$6p~|$6f4rl-N5b9tS@W9PBfc*sUkN2R?R7%9nw^20nH!>Bzg@4`HxJOa2pRc#(wv z$b4`RS5vzU>mcE8h)Vmz;qWmm%|sE|7(EcatopyQ~9;CAoCBKx~jDG8}97tXhnHn>3bW z{0*C*qh5=D2Sy7i76QX_I?*PGRlsVO=}};`nWPP4>@rn*cLiDoH74o&Ej$e^fW*#K z)fUk>nDNhn@vaYzb?x35g61Ez%7$^Ht;G3sHv4@|xU}zU-zNdQ0x0$8^yh>|y+bW^ zMo*Y6!k{wuh@1p3mHaJz>iRrEqjS5=AYIva$6QybAsT^2@Edzw<@O^9127&GVi3{n z^VF)KruMn4#iCYt`+df**=#U|Y*5-Q+*{YxF8j(FwBJAt+Ar}bd-}hz?)Il^o~R%V zu4L1^bc>+;iG{ z>HpuZX|57r%M>rp56Q!p)#_teopHhP+v{=H9Q$ zHurwFd%wxH+4;MtOCF8Z^XXSjFv(g)efPBC`dfNk>hD|WNz1NFExfjrV|8i#b*W3& zo_2*b4-8+&_WIwhS_3s#ti)WgJnnt(tPT<3q?rAvF80GYS&?@|3--VXwl#%!XB<>v zTNQYxHHfVn#Ju6%(Ba0ELi@X33hnQKQn)%D)EYgWq4yXK|3L5)yF#k1)5I>KQ?(zE zv_&>~-y?}XAesMSE{EJ^NPfnW&DvkZ)4hbGgd{NvV~fvNM;jm8#ZJHakvuW`UQY#cLA5}Yy4 zJA}h@cpN^0ka55farAQxa10_C;uz)_?igw8a*U=}#~kAv;~f=N$VrZB$23O`QL_l< zIuBcd0Cc$iTp1F|9cd2pGTuP- zKRigj_0b(T3NurAJtteJDNdD=PEIRp1nxCpXeU)8hoOg7hJL2uT;|4rEHyBoZFqdoO_)IoQEC5 zoyVLfoo5KnyM!5anJ$mxg3IIbxk8Q#SH#uN;dAwKP9~47cIxq96-8mak@#novz`IeXfy?wNxsH9iv^NNrN%U@~&~N@dOp9n@b$+u1N&dmd!@H zra2CpGm%0pWDSKxJ#vu+U2~DYbFKxhMFdMYM`XceWanjMokG{L9(h>dTIpI%u-3J~ zG0yb5Ho3OCHaSAB9jVYEKmQ##m=| z;ru1(23998Z3T^8L)Lzq24I{OX6=(vr*&dKfOYbi+BccOhp|qz!ud$n zo)(eWl!dPL`QW@Pi4)b- z4iI%x8TEk7$Ej`-r6UkV?S<2@-$|SThCaIDVDE=@>MBY^oyVolC6j7jliEF^_LhKF z=SmqL0|zJ08N(4v?e)T$S`z2Tsk2@6|Ab`hR_gp5Ns^tf2|h3PL8jFnHg!gphFxtE zd(TWi?^M!$#_APs<40g-4MG9w6KbK6c`Dc7lI_Q`fPVMx9S3pl2saR<7f_8g|(URTd~;Z8-SQ z$G~ZL`Ug8t3v17eI@iuXT{f^wQ0?-vPBz1CGP){`0w*>jJa)}t-=j`rtpH|}C{Kd?_z3dac39~&zH2t0 zvBsuFLha%Z;`VEnq_HGR&J5@`GA`+@tXaMF)NAM(q+iLqlzzx&MJ|X_@0>)z!0Fl&nCM%|%suD@9KMmye>%zuA2zkdtv(|*glzT{}a&{wjTuBe+atf_xX08Z;RVe4ujvKJ_fZ4Au$18 zAXxaG(1`apu%+j}C$v%J6@1T?SLi)dUSX8i$hbDDwSMdNu0;JT{*nbw-Hn&Qkn30mZ&fb`z7rm% z-z4JIzlSt4@dj!JepSLcxBES!n+EUTR#jRf!;icrdGEwCWSzV&f{_Eil`^iIw*1z~ zQtb!tL9I2?n#XmUh>D2WJ#2iVXCCiNxkTc5lz*2wH1LEt44>O9_{99b$5QOdA@9eV z^LJT}Ie1fzcf4;A{MOpszsr*3_k4Klww=<;Mcdw6OZoRvqPe!*%D?NpI>Z3_@jgW! z|L(xdENSM(X~K^ULiLk6bH>8ffhY{Kys8fUb3X zdo}Ik05M1uQrK#eUSO@0U0w@i{~M(ZdpMuswv_0ueTkRT=B@7pxe<086xUt5sWdmR z^DPR7$Rx} z?{LuR-!$vAftx8GyjsK3%dlEAw#NwUt)zLiyI*fa%I)In=Zgca(O9mv=kz1jW!!VM zxxcYGv|nWjgz>zz4r?Lf#C-8~*9LC1&7yIQwVOcS*y@XVs-%h19<%lLqJ_Kk)v+IY z!F%4u{3b=jdP1h-jf#cuah=~g^{)LUt9R`;UA=3+>FQnkO<3=)*2=uT#?Nt=Vl8BX zSS+sBiqnm5 zk+`PvJ*=mFmj#ch=e&n4+NI~au~*_+Y}&QsVS#uzc062*Rbx-`7m0Us-@>(6H`k`K zya_b!zK4MO^MyBomF*h8n>zun#m@HU`Ss%6e4c-`TJxK9dFV-byy3OZ?OxjbIAbjFBVdXgN-fmlbjn;c7We*2r0Mu3R7&5xqn%lPmClrCd$CwQ_^p z1b(&LDt8d4UG9?y&Yo|0$f1-U`fG?(Vp0$Ny$YW=l5Enh3p3bkUbL>r@(#sKmd zSROztM>rewrkhr&RcTWwUDAu`ZhEm!F@0jX^r8py!*UUi!c5m@LZ@kRt2SGkr!CYL zQ~0IwthQWh(pG6}v~^k=mBcn}XE$9)mayJAw%#^YZxZ8ORZYD;tloJ&1PK$br-;vNR z;;wqX6K`bd>fLL+mw`7sV<(No-U2KTU1#QN+V>M;=jun}A?_gTpfdliR) zYM|Fz_ctKCm-^;KVu@I0T~}DwmDY8&?fzQ2Zm|BxuA6LEzRzD8SS#AaKDr*H>yh|1 z7S6IuC%$pfi4*!e@g0m#yxHGz+Gdv}UG+}GoBgV`cEXO1;rxLAvhQT(Ezll9GYExDT_2yF^-h8i!$G=?rCuszB?eBD}bb8nF$*a_|XY7w5_Tsk{ z`@3%|cKzFm{nXov{k^M>9YDW1$QJv}<53D!-j}9bh_vh8qUI|06K^Z_qHDB5W9%+WCu=~>6mKkSyW#$$kx6^ZT-u5v_34mNL;_S{nXc$eu7kjC>;&&>)yz8 zc#rl3thcInKW|)0Vc5*TGaL0b=Z#5=eOrwIaC(CFFPeI5_D;d`2rodQF2OUb^LBVF z&ELqGb(xxXZM_JMxV2;al}9V3s)lk$u_uTjO;Jep4nX%VfTHb5Ihc!)Mk z8?KGiMr-4=@mhs8NvpQ7;!o3Rv{~9*inV~ofJ?MxR;(4;O2VtPwb}-4leSgcp|xxK zw1e6a?YMSIJF8vLHQl9qwE=oS59?9Azn-V(>jiot{R;GAy+j|Qm+IwOM6cAV^eOsu zeWo2-=_;(!1-8Nf9r=Gu9XO)Hmobzn1u>pG;?L&7(25m6+{XUz}5CP+?Ri{tS#S<4Jtk zPo19+tvUGD(Q<-s|DK^bu}OXy z((tW6>&t=aR43@BX6qVqB-I&I9|!$W#6o`6Nn@J&dL*>9f4%V1tbJur9Z|3*PJrOS z{Stz^I|TQj7bm#e#T^2{?LvUy!QI{6-Q6$l?y$VKyS24d@5igHt(xgRGiRo%t9#Cw zI(@poW=1WJpKwmg%G-Jl!m>uO(kQ2JTZZjCa;8a(%PX=+GLmHOO7aS@HPBGI<}6aI zY9=D?1DaPF_KEZWF*seGQut70%X&6%Xi+ziIQ(61I0?-ntjr2>zSNTa3RdSF4TFgG zb`w4DwX+s(Ae4?%KY#gNoPZtUVuajZs%hRhe;*KJ0e$l_uitX2 zHf%6ODc`Z!aM#&0aaY#n-8`eHIb>zd8;g}BdRU5!7^W)nj$&HH05+z{Q{k0Nj7OxW z^{9Hv8K;^#ZuZtL6FEG3PPJk@T+q8y(J%K_o&)T*48zVg>CA`OUPv4hbM5Nos-0RIuz-O(*9y(Q|HXd1U!b=;#pm$4LCL&h~B3RcNX;`-g~{ zqrkZ)Dn@4guJT*~*3s9H{pZ70RPG&<&zYUhS3j)H(IGUZ3T8nnOjE_7v{ zgn8|)Q8SWrMcGHIKU~-fU0@1NTa|8gWumgnz>JF9YWsHDrd*#U^o$D5_1aw^53f-6 z%ipas12phEiZF8sqjZ!I&-&I}64C1QXLNn_JjeXpM^n8t*y91o5H8#O)iNvs225Ha9W_5N(eF^b?*|l#+-8Z zN^bS?{_O=|(hGJr2@LF!@zQkx}qR}M-QQy#m;yF2G;%9r7qM^pd-U5r1+Qw#* zm086e%TbYU$}Rzbt3a)QI$|TBU$!8c`mvd>N?Q@TNm8WOIWAIAA6kf5q7>O}nUA<{%h;too=)Sh3u#Q4hl>A)H1bu?nJ@_!M!`-LS z)xIcQ6}#M8+hd-S@cfYFPKCc|HuMM~KTp!eD#4*VA(1buP(t?fN2urKC+`s*!)xTIbT!1%vW>XPzf*Wukn(_OI7R;NQwZt8}=6WGQ@EZft=` zLK@(ICJ0gE4flQQgfgWT~xBnEin`5@Y-$vNil zfI#hE_hI)DE4?ATA$=s<#-Dn;B+}G0hGvg)f1qd3Ru_%oi(p;#VORws_od(O3xhgJ z;WH3hKQ(T>J(H!-rTPP5$>4;zs-8yBhi9-0yl*Ky&>91)%%EDR6?~W@Y;sJX5Q(3EGt1EUp0krR`+*H;0}`9d1NxByEIlbR;~? z=M)AR+&Y)RH{!SpUi3?mHv1LV!@E;o2yOPn2(TT9EhBBtbng4}f7(Ars3hVc;E{fC z6@=wMaGa*N6DReG~bo?{xP$0#A?!&X;GhdxR6EL$&2( zh`N^A-Vy|_A$FrRX82a)D&t=D(AVGlfQxPi-8EuKLTkgMOL11NO3`=zBd?uaRV7y+ z;aKB*hG>3p0T$RO3kDHHUKU6n;k^5u`k?44Xg$9#FF1`a%Sclte7zs0O&DqXI>h+3 z$-qe_PaH%&H~CW@Bv&4M^368>Ca)* zGA4NLwKgiBj0M1~&}5Kd_h0B&=KTQ+Vdimv^+u7trP7*Y)BvozJsrAMzu2KQq{(gF zIhi*St-_w6+IQ+)xO%g9$vEJ7i(4zxH;3`}8gEloiFoI4tM}9CB-9KC7UDl1Sv?>s zVAX`ZCD#zzqAS$lxU^65;$&1X=*UwN*Pes~ClG{ArlJ zV(TbKkjZ?ExG9^q-0?J>2FevEczFkNd{ndXOCt5RB=z1WH2@!242TZD;euiC}e^=f!d0OPI!9vjjIwIJ4||&?+GtMdGvfYY*+m zWu($BuzD5mtD7A+HI;Q29C)as{Fb7BY0uF4ODmwU)pE0{0`6h^z6I}Tr>?wZhuo33 zKetT#@-IV&pI)~qqYmKbg7}+k#wpbo*{^J$Gx4a&mY}Ro(4cwVcH)nW0b{(cDxY4B zMi>XgfWGDCyv}wcH(jnkPj4jGNv?p%D@i7X9vpnd9o<<=@Nk05vVHW~@d9dFN%EIq z7*qrn3YRIBX2RG{Alfg}A-I(|l=)q7du>$PmyFzf1zntstbK(9ZJXj+qnUlrw>k3P znaU5ag7$mm4te#df-~29;fWmgKOU?E?M1(oLl(9xGa71@ADn;FFQ1wHb7+~aJL4*F zW^AG|Ud9(#>i{x8>Ea%F&>R0oq}K(H>(REgWE+>^6lpU98v$ifjok^$mN~N^A_-)r zJai8_pTX;>jD}nF#%aWrC0c>J+S=3MgP&ex@^YKcwp|_kJr9$xlGE+EWCXHb2$o}} zuhF7zTXR&dNfHvTRGt!m`5A7=lh@;=-8cj-G=$T;-bm-r2CaxmXTbHYyxSYMLw~lz ztUS{zQnhb-D(8l|+a${0UK^d8j~lVr18=R-tXQd;r^A-UV}2P&@^BUeQ}|Puf2xvW zEax)(jnpmQjZ*cUp@<)(VVDEPxa$-PYHxfmJW&~#+U$4I4k5eh) zxi>4n?b0ny0mrG&GY{B61^;hQ`wm21Gzz|{e2FG347%}K*Y1ys9q`6Ivw?-Rv^!|Z z`RFrUO|u~A&1jwhl`2C6V0Y`iI2()331 zH&>-YM;95mLdJ~GYm}B2ctR#a*>_5t_|kC4y?ZXxpv_0M9O`ui$HT0Lo192^$jb4! zLs2#*9kCsvjTrDI${=w+8LD9=DF+(adw=N+m+i$h*{vZjW;B2)Z+`hn)AI?*%>}1_d?`?~?(TH_0eEdhjTiA!M)kmz9D|Wt+DUFD?2mAilL%?0|YYt!dm_Pe!rl-6M9g*(-Z4;b_C3l@H{bIdB2fd z!Mt%p+~9t-{(w>l{DM;ST}Wv&b{|FH3Px1KR|*4+hfK%Q{Nso5<278AzEHr?F80?p z?_UA>LRrMb>D2f~d0&Q)^ROdtrjc;|yQ zG5|mypJOD~aPPZtZ?tf4RLDGbZTO$)@IQ*-&ZsztX*f-~Uz&7rnh0q@oEi|TUm#9( z$dfF%BpCp7k|@Uw*JV^@;+81a2-iIe*Db?o68O@z{7*L{T-O$-X_*#O2ZS8`8=S-3 zy;6Z3s8%)f1z!mRe7RQaRufcN9>+vNiV2^%aZ_l_7crP;m)sL znx5!Es6QZtg@c^Z;1Tp)!qP!baj5S0vaD}wD?^@RWfaaSwKE};UrTlk3cX=Tv*UD%R+ zKAmL4_eR-6#5`7fn!9k-DY>2an=HH%OkIQ0QW@RM9uVkWgP^QPR^z@|X#Ta&*(~-7 zJP*NP-Q2p2W171ej8p9SUE4|zQ^^#5ty0NTWvXKXxw>*p<4ptUL5Pi9&$waM5|t#HKb&A@nu z;7+%wNu5tU-O4*%diG1nC-V3x)fhzT1=a@$&GBbhUWY=>nWX;^ z4=I|%%k!-d`BKCeW}-Nf?Xqca5Sx?jvWlGP*CZQeNSyK36lM+#J;ineHJmA{%o6cPk$>8?7W-7bF238XBLw0Au~{XA2>4c2PS+8l2aCZ>Er)u z4vG0<#tTLcK!l3V)1tF6Xn8$_!l>C@&Tyz%l$JCk5>hiPe2LLH*au$-MGK~+pE#qk zHl{TBlKQ9MohcOl(I4J(fSIx{omur{&6Rn5BLFGFt>?K^(Y;|DLCVD_kz*f|Y!Q;B zZ2K=(YbUn9pP|d}5q562FxvXT9+TXD{;W^9)p-2}!rvjDSWkzRpy0IM(tE_cRd~|4 zO`insac`DjKuN2V!twhMDh~-?Dtm&&EdtDK9aKuz-M?4z@cCG($*iWuEb0Vu`Gl(J zMP0KdR_rdxOY#DdO^IwHhN}sKrDpTp2iCChqaPnRL&8%3i9dLe)Qs!{I;xl0Fq&;3 znJhVe)N+r(DY~gs*QDA|AhGAAch$s>{n6V=AuD^s4;b?P0YrD(g8qzpaL?e1d*p^_ z1l!DDU0qs3R_o1a(N?-bG;GmOxm9Lqr&;~AKFS96DDXkRegZ7^XK1CvXCqf$!;-HjEmXA=N}`b$L&e8qKpD|GgcRG{e4zt@*t)Y-8@q){@*d>KiJ6#(p@;8Y;~8 zR}~}B+dV^Rx%r#V5(4=KtK-7!fe`4g53$ zPH?*=5kIk1w-TkF46nuF%ugsn;>KF&~g6ZlU-L) zEvkc1)Fmv-k$hz?s(~Map{ZECdtw`5Y}2HDRIJnn>6nR>;9S^EI<0`!XZ;g%c8e%Dvk^Rr6h;#2 zr>-ifY(F4xe&$eVY=5OAklVZAeo_R)nthG_3@=Y969@h0(=Zi5pi4W{od?engF1Nh ztJ6|S&t;^|$QDD`+c$zvf(spjm9mCaD;fV7ny^S3g06yJYNGaCdBYd13G#Bo zbGcTYa!i+4%qyTGV(-ses`_;AQn-3-V0qbP=OQ|9hD~E6GV3Aj`qMaBm4y1UN!gMM zkF=8!!gr}xgFENyOq2&$xeG^rjNe=$cVX2e51W`Zov^;E-zxtR2rR>+Y9bUZa;d=_ z|7eQzDTKz&HmiyX{fLYLRbtb3seFA4D_aEiYp20^ITAATYgH2Le<=a$&D(8STqqu9 zytNjm&9~-83cap4@hWK19nPd=vkd@gUisT|p0>On^<#akk zaq4I*dTyx}zV&w4JCgB63CQh;aprGCG$qF@c1}K6uy0?yk&eu zrMb1u^kT=Xu zPL^k=iPakZ$|fU^{7h1ggpKlL7)6bVonGongdznI8B_2_=uyCT6Le=VF>K}KiP67^ zEOkhzMVKxkNVk_M@XjU0EIReX>@n-^^(E;w!1jn;o0kHXwB;1jRdN3D?& zdmvO)NTN|+A4clPxx2{tHkKppWF7#*%!Hi;tH)Fh)%@LeN6Qx*_Tb(KFe&bcGpW99 z^{=FlQ)D63I-ne90a}mxV`cX(>^R_KWdOBCZU@(_ERT@FP{BlDho$PD@2^(CMiBe^<4Oi+2 zZgtG!{t3S#c6`uGG%H5lGhgd`?hSM7BL(!>-Q$DGp^?+SU5k!To%Pp45J`T3jNsAK;OxY{vYwkU^IT$9*+369-0cMrut z#EpO?b4uqu9}f=%QRZ0r3|N-G=$pIy_g&uj=*lG0e7|E5NRh6_p@mXoc6I`oAxT$Z zoxm&WN0UvWf4Htt8~p{BhymyOGmoD)sUO(AaWAfaaC7_bUo<>?Ph)f-Gk+1-&fL;X zPx@gjumz{_W5me>4mK-WpatG)@_R-SKvPU`8M=bnVt0xBSIwka#t`)F5yeCo#_N5?sFx(1qKzN|tYK=vGiHoK5|NRB@8)i$$A^k$y9pRA%@>G)2 zZgXuGZ8JFvU$ku-Ucy}%Z_{21T*zO>{e^ug{(!WCn!tA1rrbpDE!dXZoap1GOLU;i z#!}y8RZ}y~WrMaWWdmx#HmEEWlNYPbDf1Sl1@IRUmS{uS=YxQCuxCYADvwb28n-`g zVb`TNrFYT23+bdeksqYh8?CS|kcvOeId4uCCl_mOI;mZ&BX#bmkEoXfP#4Fk(RRkG z7ZvMux)t6PqrnOn^5yfv3y38YHNy$Wxu_?gyadxJ<-zI~dj9i7eyKWYIzqizu*$|o zD%hJ-vxQszVOz?DXqR8FvI5hfyr9ev)u75>L!#{e+~)80!>hy#>R$a` z868GQL{N!R++#D(r+ z?1<#3t#f7;ac;oBpoD!kxpShhW)>J=USdC(0rZj6EA)kWX@9DE;=M3GS|EgZNe^E5 z(QP?9c^nl0oc(KA>aSyA!{Oq;FuiD*=5NhePI&O1l26EudgB)rk{^ih)RAA)4}^U+ zn2}=_`C#mt*#8JQdOMw+5EdaO-Wso#s1w>%VhX=v{b@`1aOR@6(WrV zkO}=tQB4ZI4K0;2fdvp@Ecp*gigN*~F~KI{ahNiaCfI;fjHMuHWB_ewF8LKKK=?mM zD&|t;pqY3*c{MzkF*Fwt7cNZ)(7{{^AJh=9r>I5++lJamneYQvFqZ-b(ZpTJtFgi9 zp(o-dcz_m+rQd^g;;xj{L|_i_X$%=L6Lf$V#!}#*zW5!W8V~FfdLm{*26)C?iW&43 zMm3EG))Xb4A{ryjT!V1XCendgHb|F zDB^xg;{xz7n?eUA#F;2SNMN~8T}cQVAPI9w@+%S8M!cLH1P`VU)s=wI0f3lI!5@x> za*DV>X*2+DsIC-*53qpQ6flS?ZbyEF4G^aIAWfkVKwQM2g18+3gbmgSUHuNh1vFqb zMGe}DUttW1eMJX*i(g?5N##O;VZ@O!ngRz&#Lvi8LZvAHze86gAWQ(ZxgpkwSSHmw zQWcW3p7`;sa==^{kFq2h1wfTVlKYU&^qgWY%%Vr4c1zj?uH4!+>!@OGQkm0Q8tzVT1Rf-Z3BK zpBu&$KlxnP0g3nx+J_{bh?s4#ACjz#il6&`Nb>))`$~J}d$atKm$Zx3Nz=#i|IR@# zDB%2nNbb&}Y>O8&mjexucGdXIoA=EqKgN%xrpaGmPx|HLC za=zvAAxSA*fY&P=VllgQOEfc=4~i6+H551&&31jSYM^6x$8XZtu@CI&ZaT z;McYMiPJ-o60crK>Efl~!oIOKmW+On6G@C0Z^f%kw&`i8)&zWaPK5)XA>aoTlG|;_ ze_m9>Xa8f|(1ZU^p!d>C20q*Gh*nt1WXlg`5~=Z*2izGV&HD(y$6!g%;5GTbGsg{F zg!i3$u@Wll0&I3F-&Gu|ThQ6L@yR*_@gl zz+zXXCuQBsiNqxN>oY+M>cDu`z}@^rvKUFqI+qpkOYj$>gML+kdCG>n5k|7uNy^%n z5wVZ=C;bN9CJXc65v>PJY((rzbs@kIvKn`lE*gHWGIH;z=^EpWP(J669}Uph95keg z#?{#TWN2E%&v8|QL+TT+YrkEh1$kA)^%&R7+}1PyXnm$ zYKL2d0xhG5Ur-uf3Gsf}O}DwMRlTRZ3BF(L8g}g#ysUvT~ zyRd+e9B+G)ikQ%FymOLLJ|dOetw%+i`U(4bLvRIK>hZPobyLN)eunR4G|ZWt4Xkl> zR;F_HgkN5%-vrYpw#s8;*RZ~zXy5#i>NwNm>+M^yZtB|>$X3NHbrHiO#2jV!6%iRjK4-gv<=SO|TYD%T`5%>Z38m=e3Z zKiU{SYPov2a<)inp}JAGq$(IsRP86=INEm~s4yoG;|(5TI%~*YW*R8-(C|Tamy0KrUiw2^PQYABNcXkGcyTifPV^3RmeVpVDa+AUT(4r(bAs zJ6PF(X7b6ROsPX(9@4t2xhURyN7LVPh2CtEg3;|$UJbz1qTm6zhp*Md=N}mY(=Y0J zBVf^WJl*af=Juc`zSi5mg{?B?b~JmxOBc=RAl9!~vX4rMKRTG}eVLDhnNIy0f~pL> zk#^pqu&NK@nM+orHwt(6|Bz{1(zoyhy`VL+?lq!z>=eofbL&TWE$pXlGt*|W`slS++)*XRuQXDYVG@`(yN9+bIA|g?$#LX>Nvqlf(1J>vmJyxl_zdT zfX8IUX)3ngDmaEk;gxC7vGJYxE9j)-RSX-m5CBC2+j-ls^9aTX1m*AL<#Jv7QiBCGY+ z3;v>$(U)jmwX0?0fvc0D7s7!=YX#i`eJGhbB+X5sAo>J5Jto~it{~BjbI6su0zMOI z#y(`qZ2{*~tOYtVPy0yUQwsWXkQ^%K4#bj5dBPmVNVCUghzBa=ByQf;jLF6>=M5*CkUzILIuar}?E(!0QRLKs?&v zL6W#4mOOF4zIxoMG6@@NN6|3fNLxlDoruMGKz-eGM-re97cYrYFTj~<`W*b$geszd zp^SZVCnEkITznP|lT-9Rn<5pa;dKT5%c{nwMOAmRnqF3=f}Haigw_>7d4`;lYzbx* z=}|X2xlXZ0F|n0W(jZ1mD(ye-5TDG&tS-tB`VEEtgN~8sHO*RN6aq{0HBTeW%5;~N zN<+$%(zcgwsW(zvZX1WcZ`@S&!3PUO6m!P@M90kPg5QbseBug=@Mf6yBzPtJzDWYf zpeG#?4T(prN5WD>Sx^5O5>K$fOhw~b`hd5R4epeaRpDT%4dBq7b$MpX4) z+yk=9$e{0fVRtt~*cdz@clQHnDax{}&&6~_-odD-NF=FB|_wYzRna@tm$NBBksnb6`Pa4jz!Gy#F>6W$WG*r97P}FJU@1_ygDby-P zN2TkM5ZwmkVt*w`$mM#e`J?oh{v5bbHx;Qc6#naJKRfvf&S6-R{c}^>flBmaHBLKg zmln2JG9%%v^-&xP8E93qr7*lNI0SFpJJX&Z!H2)Ux_zxqv!h0JG@GyNQ{+K=+i<&t zDpyRDodJZU{s)f!I%vB!3*CMKjceTd`K4cU-}>51*l+D!-}z>8p1_S;zGRNH;}lY>0YgQt?iZtN-~zsaIl#xpke_xZEuNkZ0g(Q@%yrS|F8<%4DB3Pe?vy#Q-s*9< z`M3J~vP^B;aunrYjchgBR^{)RrUoW01wBU&6QexUg=g(1Wl*^Ot^RbGYk5A}rLJxl0ZgamXA#Sk6MUFz99eh2XlS^VH7^t(_MJqb zw*M;Ydmcy;H9e(~qv-r(d9VpwQ=gJ%jJhswjb*9(V73YI;mQSyYVM~`6o)K0&@sqP zeYo2S``xBor(NZ3G8eh>6-3-03iDA3KO>>Ie)90s3ZclNK$VR8^k)hor~7+IGzpea zKJ*{t8@yV)FK+)z8}F7S@3eu4Jkl%sZL^;%5UtNDCpw;T-blBw>phl)uj)TZ7Jh!(4=U{BOUvA`l<@HMZtrzpY&FF*ao9E4y$<$DFuS@v$X+~pp139f zyJXhwu=nh&ojH$pP)_LP@cxfp0C!Mc;B?X){dGCQqXWU|nEd;w4IkQbm zSSeBmP^&qZ)4U&OqG<6R^^VtYbNaG+xGJ(Ki2Q{9Md3;X%pK=7xX(^>4xp5A0_P_s z#$Ff_du2-IbmE%HeD9G^e+T}vg?)t$dH9W9W5>xQK+eIy;%!tl2D>bMe3a`le`K2x zA#@0!em~}VtSH?O?8#ck4mlS{eAf_b)nJnMn4;WlADD9*@N!vdv|IfX%jLz)mE2)m zHdg}d$wrZ{*=xVmET2c!iM3qtdCDp}@(y_-(w^g9X=~mYywj(oD*-W^sc}vwH;{cITt8S=V|0co^r++BY3}JI%B&*>~{hZ(nCL=szudcX=p!TIskN z^*dR53oYt6mx+K)9B4*ABe(-SWTecGH@(KsGZI!7DMFS;Ry5YPT+x~u$9Q(6`%koM z?>XiIH$Ia|YB*mO|8<@$IA@mOZD>zej+)4rx~rbbz;1>}Z6GY6ydtD8Dp#Ce5#qwG zKvIcxesl5Z;d7{KW+nesKLQ#1T+8Y+!_$XLLQ%^P|@5zeL|mV#>pJFN-s+*Wu)jQlMKcd z#-(mGZ^c^I@|7DG9^s-uYoU2kb9_wl=-=2qpQ*hl zGYQ$7?WFOJxf-3;!tL~5V*6-}gM)7C?t*lu!8p8scd?px$7{nP`Zo-j1s@-emc}*I zUo{Huiwxh~$Ai1{Rz3sw8*I$#HuGGAEhVuVa_NItN*3B`?9?}jQ{m0KvCqn_R-P;y z{`BnD1Jq)>OUsrpZ z4>gUbE|@<|;VThasFMq1cLg5*w8L+_9FC&I#9NM$UBB&VudEl7H=s7HPz?8|>>4y) ziq%_zF%J{=?3ZV=F5i8Odg;LcssaK9Jl%x2}U}2rwaBXZ^VQX zQu)o3UnlrU(@T6D_x(E;tQFkN+Dk}Y_ifp-vY2ON<8dKqeL-QkGGD# zTl|WEqtEA9(?9)CH9(R>A7Yt99}^VNfjfKGVN?F@@Yev~DIF=BFczIXNH{+A&%_;u z>M=Hs7`2YdudLMeVoQ8|D!8GZJqqRM7SiHhg8J5PM(m)~_&Fc5u4LV> z?3Tu{IUuKGo_C6;8gLjy!uYv4jrjB-{z4rWya$RQ%{iYoeR3v1@0DVQ^d`3=l8`NAhR1?y!tW>&2zx!;ZXs!PzCPlbp9dcR|nYd^$ zq})MlcDZW8LaA=`u5GyQ5fOa6+Pa!vk7}t!&&Tt7uGL-j&)!DYTT@rF zUd`S>#VZ<+qM>%jG1FOh2Bx zTEiGcF6*Z=Eg-&x3*(j zgh!TvR#GPtpajsnEUNsWwb7F|+A*?R^OL52QDg14g#UQe=lkbIj+8@2+4IzyPv2)O z@=0dP5MMSCo=Qyw+qifC#`ON9^f5x`a5u-BJ8sSt?H!_u*Y7~l{=vWEGidazQ5BeW zI-@yduiR5i-#tdhD=LoY7xhzqXZv7#b9J&RCNah&**r`cxFD%FFKOzNsBc_RRuMyK zBmA;1WTRQRF_=ZQgH+KO+j(IqMq+JgdmEzjgzx~5PGo$#QkLqHYL}ksX+gxl#NXoO zx!7R2vp!svUmx7guI)WI&&^`;*QdduBUX8SE%vE-Fq=bPeobR(3+<5IW z+nBjyM*;Wp=GboyQ^b5#Avp<}IL8DAVeM^|OKt}#Jd5IfMcJ3rgM-|!?@IME251hM z^ZadFoo`h{lqnwt+}p@+J4-iI>=lHq9ZuCokX{>Hmn`Q!pGMLS43s#1jtM-59VQ^Z z>deVF(-v-~QZ;W={}C()r>fZ6b8S7xAp2C#ZVvU52s2e{mKPnXwvGQr91%oh(7_yA zU0iHAhnzOxwvb*O`wFpsv$LP-FL28IJUUWDJWu2F9b512$!eJ{V}(faoAYT7s&OML zqH)OTP!mb@#6OK^)1HlT(oS#lYbNIldp*TBvjwZuaF=K)8+y(wUL||Gy*luz6JA|4 zs9|N^=bkWMG&Q|ka66{Sa@vQ1z1YNb;n2NvbhpK+pqXjCs_y&y)sFpw96z4>-5sIV zKBAA`JUs8cww9aRV zn>KamH1vQrht3-7#EZuA$$S@LptY6Nd#uk_hxgWCS4UMzenlQ`{&G3FYV{-TC6=&5 zl7cYrn~@gi_~#hX$=01MM^`b}n)>%+uL&<>AMS6fr?^SAhianAPiOX|X)#UlybE-c z=MdWV&A#%!_Tc_xonEj|$1>5~OXA<%fpN>xbr%AXi*Jve<=h^}5_lcPJsS!)R(cP2 z#b-K*9V3c&Me@fU?7-g$h2?jaCxUN=)qSoljwr#Wls;a^kS~u@!`|)PQdIU1-*!TK%6j<^zu$XOy|Ov_ z5*p};bMV@-qMlW8rmXulc!CIjx8-&frs7d5n(DS=6KH9Yj=fi51h`+j@!Ijo{RsBD z2shlDGq*qFaZbG7EPLIEu9$yuoT#EjG;47K9eZZIfU5c~EIS^^_O=B9;p1-J{8!`C zsyf^P>1r{2LgR-F<13u1r`f4L!JhU*OJ;6F?k9ThC;gMK*2$FfDe&0i?Mr?Sxl-@Y z-$n?sA`}Yl6OsEx9vc$Cm^Cz7SAgsI|mW`M5XJ!!h@FK`!Im}XtNa1v5diyXg zjAZXImN9B1^=CLrr{wmaaRMN&9Po)MgCAOr8fw4zMi;LRKXt&tCxnb+8f3hJ{VjA-0q#)sr_2lZeb2 zB;okeEoP50XbPUuI)Z#!X?nhiL;4-JIEu_|R*SBdkjrcHg-qs@r`w$sgQ)q{6S9n- zId>y6xAn+^7Z9!{j|JsASJ4@~o@K?!w$(|ynTXZOPaT+9JKU1#`V}&k)#qJdGr%0T zp!7QLM6ST9@Rz!yQqOjU`rEM~ngySsT>F0SJSO?dj`y{)U^JZLH&A1M{pk@in+o6l ztjtRDrRPii?OBOZM#*&XOxa)HZDM)RW<^^g^J>w~7)89F7F&zx99fC|VVl!6YK&P# zo0Nf8f=jEH?@zl4N<2_q$VLY3$&GcB*lWwGj-h&(E>@c!D4e5?k;y*B zp-Sa^%(2baadUO`BKefQJlu?8)i8BviU%&NBGo(eyGMUjdwqE#9l_u?J-BYRYz-=>`d{&H0~)$Gk5UU*1=UxAf402{ zK++@MRLZo4qEySO^>5$!vEw!K8+Fyb6oF1JJC7_|5=}kzQ~k`waQ1%=7~V!_kPtz5 ztHgAxbw*rl(K-3IXIx3++j0*x2s)n7_?|sGtDM%j1jn1I?+Y4S5On0L8>bICBPH=h zE5s+*H)a}CTV96Hmm}@T+`BINr^)0exLk+qr*qZcg|yd>G{#W(QPI5TZC;QEA_rL- zS}V-a_@>kNB%OB2V`n!N^5Ngt=3jbv>LHBz7Wskmtq{R{P@Wyf%NbYP(k)^lC&wM* zap?Nm{&;1_(V^w4TV%g^e!zP~iu#WA$-HD66Bv4>ll7>b>!_ToScJ&Mzu{~-&XQ~Y z?e9fGPIt>9Rq)N}-zAP0J!Q#EAt$_;B7w~iU0bzoyh&6ceMj<_kyrQ+NZHR;)9(A= ztr*X9i`ZGCr}#5$$(yzY-qiNyeSwz9ocMEJ+qSBG3d{G-Jyvt9D$r~w25_OmSe_{F zy6ZSxet2%FrQU3p>yGbS_*C;~sF;7REYi-)yYg8qe)s)q!?mIf zm^9GjQHnq2RvD5Y9<~9lD zbfM480zvGE>nkSPiKLGeP5Xf%sOzx;1Fw7V?bbdU6bm^{c&?rVFA&|i$`wSta&BJN z<=x-LHCIb6AbpPJHycrHE7@c73cOFor~QZwXye5%M@RD%A@bbCdJ&~&9Yg#soBLDE z{CL!h;|d*_mQ78z;j!}vTeE|>ta`S5muue@a%xDJ8F?@IuM4bqK0W$9cDyX0BAeQo zI5~q%4Q)|BTJ}a(s66B>S&Zgp~#`Y$r%<`so=FS%69PFGz zLjUVMWa%*7_%*v67L4Bprc5n(I0)~YDnWmy#fK#t=FFa<;L^Snu?|w;c|3CpX}Gz9 zp-4QU_xXcAAg)K&(b;j%4NuVQ@@F=mH_LYzoui}A1l;N9k3#Im=>%Kk_?0ooLY`e* zliGx%YV}nw>6!HR9STAegh|)X>DIrWcW+MITuc#X3$5lKj_9Rj%KXq%Jy&$hS#W(? z(kI3lx@$b&d1Bch%h|W@JAZ~0$~KSU8=se}t~5@o4aS(U7T3k?MZKM8L^~)mQ)e zC2BN*>!dL7O2fh{KNE9t-4Nh$Ietwx{uNehhhZ%njaiK)Sn3`xc|cLy#F1$vvG3$Uv`h3u8 z7-zSJKr!j^8MH{2cnVFdGxRyamiRulA=4-U?OWwm$c@a?SEWVD7U7CNq-RAy^{d$(jyEWU~7150FC_2R32hSz7I$DEV<~N z-pn|7M%<0Ac!2d~lH;etKS^e~)?y@4v!!y*KMj^k;Ju1%C!|m6dvQrq>zl-X`U_V> zvzcfJ3o~g61IAxQ$Ta8BpLF`_npag_lushQ&7i6dIghBZ3bK&=jh1(lkCDIk*vot7 zOOJ*FT0ap}HVr3@x@u6)Z{03-A(GEM^;ST^LRFIbp#K+2WM%p9Xpx(Pg`M|*4JB4` zHZCp}4i;uL4+m3b)&Crd|3}5{X(w%nKfjnh3BR?fy1CYm-o+`na;MsOQXtV{48lOe z1Wd9*=ZH3k`hV=Mm6B)w6s9i>)q|x$n60@7)wj%CGq{E9s+DRg4xPPwY^;5C7+8Dv z1$j56T_>MhCwiFgrQiEx?E7T6ttUf4Los7PYb+??v%cgO|MuOy*GI%?w{w4;H1uO@ zpVRTX*u~Nj64dTD zC{?4;ptx##EbdJBkLX=GTjP+(6nr1kpMn|o6#4Yj3o&-2UVC8%Fuj$nr&ov91&phI zfcl>yFs-Eb4>zWA-+QF+&JaMHiFoE?+B{xwG1NGING7i`P%0ZsGq}uf%rx%pTmqR~ z&$&1?<@z1xoaEfCNG$JbyzG0-8=luu*HHbo2r9OJ*`1annWroIj={G#Pfjd7Y^zuJ z`I)`W^J$NAiSM>QrWji9i2bKi_r?TvP1gTfATljSo30C6cHLNNH#)#Mm8#j>za1qj*X6O+jcr;$F|e4 zZQHhO+qP{dJM0)I_w(HEIp-VS`Bi(4vBugpudAxooV8ZXQl{Su_&c!`deULK=RYKc zglS8R_SpR`g^;)ZUe`+{^tjK&fPOHi>K++ zYaf5t0_Vna_B*nz>kepquRI=xjX&Wqx3~Mr$nEeG0W1^F4<{yC!&fMk0B}-Qnvwo7 z*And8b{xRbOOp|Cs2*a?&k4eS!a7^%FRID?OYk^FQojR^AmnWbDPX;q#X~0`vS>V{ zel?0Wk=wt|jD=cw;>{zzJa^kqqe{Q+*t--F3zdx9(hp;XeB`uPP|yanIB~Ook`F`~ z!ToII-u+Go4H|oLc_woe%TZ}1Q~5|xX~BPiw%Xt(>%PuBMkFBjj9fH@&h%Qp%M3l& z_3P#;m-o5zfFvOBpv|!Aw1X7ueZp>j5hPm;-{?!X=z9|5rHE%E@85|8X21Q6^a1(; zcAchtu0a2RX6TCaDLZJ{Y_OVgoM5UapsF6Jy8kz%_AWv{xR&FV(u(;Hxw|Jap4dzs z)&YB+gK)m$58SVdUT^*n{5t>vI)0qx7Eli`E|=r!Y@Tok{QvvMbC9qrtcf2yZWqcS z*7tQIKD7HWpV-ED{=|^_jFozOYP(AcWdcj8i5QF%+>S?eedaVH` zNPl<>4KQS~zC<;$0R@(4fg)kr{a~{sJU}Hd_+rDwL|sSruQ0PY}{-9P&9ne*!O#z=Wi!~Tp&=2gpPC*Z%g_kZe6fXIfxM1go(eWdcY64Ajd=qN)r3) zw~%inc5%E%(IPjhV-CfH0+5L|*;sxgB_eyM!8uJl7(&v*oV4G(OHr*7p@j7qd=nan zWT?FT5}}Jk?!S<6NF$zVpIZI>Xe0PSdB=J=6TT)m*AdH$2 zKk*PIF4%JhACAFTwqAll;#hAWVts~rQ4D`a6j%{1tYAVb14md`F)lQ$*ywb5lVYmw7_DuoVv9EIdU*z<0Y*`N!t(;>*Q z*{}=9_yfSA=>3s{VzrPaM7&@ogxZs*NT~sil#?Ruu*ZbFNpnZG3j4xpK=*OgfeCTd z1`jN1z7LLSsn0*AeMQ7)0u_-w^jK*`-O*?S-PveF+~H}2-RTNW`pUw3dS7aB&al+{ z9BB*Cd#MU9b{hwC_JlZa&)h}n{h4ERU`@$BL9z8%=c6KZfK7?JP^Kk15mNfx#OX~R zpgHi*l4`-vl*M-fbQ#@QwR|72)IuIKS`p8jYVpqgMsEK=C*2N`sRcSSSq*&lc#7Vc5!}_hngGcdAZ%CL8%+c&gchb(WicFasH4U zq7Pv2kOwKz9{(NU53FsWSGae=TcS(D2TfP(Gnbo@L6{rf&IDIHZ(oonOfJSTVfc_Gn=R1^M0 zS$ZR`uF$gwgW?4#q&N8zMUAj@FZ}ZdKJhZyou{wDg*QUnL5ig_-4EofPt=7oUB>?k zrmRoW@6oq7>Zw}bH|6?pDE>!y(J!2Nq{;3~IKJu>FFY}2b*G#?bbaM6@Edb|D1Kk^ zuh8fEK>i;6|3t!9s4Rcn*#mN#Y|e<|tAupP%on@kE2VS^y$P4s*!O>_;swj{f8W$0 zO_o3A?BU)itNP;M%U9ON@9Y6R_5UT@<7B@!e4=D`zHi7^ywLN)KX;~k%B8(>)-L-+ z&h-ITys)G9AEEm_52xaVf5cxO&61vR$JYw!{|bJolAcM&*NN}p|0TqBejs=X@!AsiF8g3OEoYZCASy4bC8R{+Vi@!Tlwb2c4m z?oz6lr18%Cv&fAj9kE?nAQFerP+s?ARa38?LgGtI1@81Kbn+{%qcJ^mUnxLPFh%Tl ztadCF4H)>6o_LlBn>>&{Q-6Uf$;9c`D3Z8X6oOh8!qjH_SHb_N;P_RNA_S|d!ofYEJQF$L?}4;I{C=iCPV4bO05zd2O}CK3wq&C zKlE>FA!oM`Vp>v=B}Lq9Ylk3`n6m9#{b`k{m2&;>}-dvqt*2n(F2AX z#52EW!4idk+zY=@%9rrUCx(Kh5A0YX;OkxLfi3U(sNX_0;WMW1&!Na~gH8R@a{=LS z{zy?}Rw^>o1n>q6rgR1{3aa5nbyyVOgrD2e2ShS{d!@I1JvTnl>*+lIU059WKtMmx zoqoze^NQ-oNh>RNQ29YE0BhLl#e!}NlyoaP?35b}ruOAxNQW41;BHZx)lGm7j{al< z_sEccx6k90Ui^v^s4Lz2jG15ZlMGi>UJ9g(84Hvc^8cLw)zbCCf0NDl8{*@r~ABNMU;gMhYQ_)a<-jf1VaS^xkh!X$xk;X7C z16lrjhdup_iT*-b%`_PYJN;5qel5p7h{p31zZIMRV$Aq;{FSQAeDCw;8@b7ovs)jM zL_83RZ$484ansxWx-9&6NfkX14ETCbaKv^S&Q2Zs)fPeUfF?aA=srhllNC_2H zv1|S7>}7M{r-xuc3)B!jI($peD?gJvvq|2>*Kfk3)ly3I{y)uXlVlZO3FRhr=~bz~ z4O@R?vqY4m%Bjj5TSIl?$|XgA@{%0g zY*3@Ij(}B>jwla&sSj1j8ejelg{b^s#Ps-QzBpT?9A6NhayH1}x^!B6aI0U2Y@9Uy zG}ss}skVj;Mb85Ub@>wo>Tz~5Y!xPR3}9#}MHl?^%yqG;k)dqJLMkE&(C6Pekm-kb z3(I}6{kxvDTV)Wu_6K|w>Ydlm%kUm&D=&Da1F=X)n9cZ&5rmT^to>1-HDM`>RB0G8 z>PuV;RN0>CdHAk~b?1BfarZyu@4>B7qEqNOo9FlCG5{Ea&-Jr%w7g zW;K*(z^F#6$7kWGcS`p4<712EU*D+B`!7oWQ3yxYbkrOaz1X<%q}L*mv_iQ-o%V5Y zvt`R-r4nuGyk5iSvnZzeYvJDc?b~j;{$n?1X@J77f*0KQxzq=r#%lOQ1-ih2J$uee zJoH!o^pzt!R|c?X-+?%;h+=itsa*3`-A4<^f~8#bJzJy0n_j#Rod2)g!u{8DP8sjq z4d`097pnXr7MRu@a?9!~&Dk8?aGY7nXy`hG9rVVl-5LjKZ({(|ki&GyH=|v-+Z%!V zDG~C`Deiq#s~~g|$}*GO&HqrwdnDAVg%>k%cAv{_BzOO;#V74YeP~}aZO~%r++k3{ zL{ze7<=jzD=?vFca17Dlj{kZ^{C{Dz=erHI^(E1_ZCteBG07XVl5xfAhp+(t4U;v~ zsB_1F`HGfo<3}Z4`y&J{!3dD0k2iSqw_dzC9vlYrrBDAvGNR0bg3lPJ%QNc-pUIF% zpH1tUm8%DjvM=PD*DY^pKkh^4l4*kyK7?0(#}CbP+`M&^4!A#JQwSzV|0jC>^hf@N zX9mCMKXi5S=|<0hdLMsbqBd_ z>c@=q)t zQ$TS3`>go*3-+;r^KT5-AAtK$IPS69DAlM6hma3vfb*zaJFmd$QilY+) ztS5Evnx)gSuxD4?0Y7ld+g_vhy#-RBtMw(|GN{pD9l#@4!)BxS zfC6J;0{L)&Y#Gau5aU1^LIbz?37pe>b9cb#RvJn16QO^f$xUmQ%fhqw-0QT4{&eT~ z-#sKeHs7iCEdN*fKT#I<-Xzf9%O5L{u_~Vz0#8PnM<-|JK=K|NC=>IOMJ|2j;Lf8T zZ{{D5_xdh+HwHF1gv1QcnVsX7*TT%h7g+4gL076&;eMmZ$7cukIVTS3)8oK_H40)p ziwSPZ!1YrbmGe z3}Acnwj55$0=?*vYQR7bK8x=H1hFRf>cD(Acuix3|B2S0R{!q14{GCpx?o;UbQK{T z)zAF!9O$Xjx9B&3I8)gQcp-p7)-WKys|LH}XF+$zpo4)49etVL)!y!zZ5?$5Yn{@3 z#Qcb55g$X1oO%n{XUwxy-`DSJZN-$Trh>4A0h!$g!4uHCDp$&ys~mp zor!6liK!CP(JQlz0YqP}!KBddtMsL>ay!Jtn)_eaxZsfq|Dn_;QAji;DSOTkaCCUI ze8feFzmNvMXTRGY$!YnivBE`#&f?_lPQMi_c!O>(m;mLm|CiUH*kYz}FrCqATBeRI z?_u1vRz{r6N=j)Kvd`wco$bY1Y=#*4Cmg|4@(1D6{ovosCWh zP8JM9d}H^?B4hKW7xiCeZ*@bpPPIAOXYf8FV&Te`xYxFhbs8HxE6XM!P*a=pcp=vK zGFZ9OGg7kDV@rTQrS75YVHlE9w%EDfPIO;OKQzU`T~eSz1Tu z#aA%7eVJctskyf^9F_Cxt*Wf7udi&?B_+FuP#3&D4+Nxi8k9CY?oE)5fyy~yVT@Sze z4kYW!D`nzeDgJ>7RYH3jyHLzTr=6~tnt6Lk;cKrUc>Z@mT>HcE0bV+lE!O)7BW}2u z%6D7}^+Kz|U=QA3gxD-Ex3H`jhgdSGZ|!!LUm}neTv{zJUsMl|xQ~e79a0j#6pr}m z=P75$msD8*s$%m+=mIA2KZ}XfQ;>grBu0S(V6PqFG1;}=?zaFAu8&x(J)|><(0(~Y z>NOs5Pd$& zS$`~C}{KLcfNlUe%kGic*z9BXdf8a*PUD7qBlG* zdJbkP=rn;S#*sPDd0?@LQLgxT8%qcWCk)QQs#otWB)kwyCfyg?Oem~iT2@V0Q4O!8 zN^ICMqDtIv?03#|s)PUJ1OFXwdh*c0|4s9a=;!l4(BW*EB7BLI29GNhj%-?DEVVW= zLd&E+Dwh~~FP>Cam*L7Qvzg_h=N9Vk=9UYBl#fFwMU`>Ejy#=ydd%AXYn6{6xLx3$ z=jcEB=X3O-4G%35cTsZ@?u9T4bb}@WJt+bWCMgH#@PLr>z>fQw)POSdxz7f=P!)to z^MdkF*XYTReq4rD0*Ed25F`D^7~o|6k3zVJZc}{fWJ3JHXzlK9L-6@u$3${6?@xxx z)<60E#Am%Q_Beh3u3=`25e3;OM3;=fsaJb}%2IOMyG>G(aHqzmCg>@C1<9|MRCeau z7^5yeTO~(BV`4`*j$|SoINq%VYW(M)(jq(mFU~5-IVafLjPWJe!nx5k^5nwb11BJ3tBA^L!frC zHr6~{!$jcm{DvQl@CG!#c1yQ#0P;GtG&2!%G496Zw47z$N-gTH)5Qia^^>bdysQ0N z>@h0@K;~ct?(a0*++n+urO;Kz2rGUoyR^9!Og2c&QGG~L6?Slnj-2M4rax-lQb|jE zXIWZY9!ts9pnL}sCQe!dV36$+@vGe#qlc(->pJF2vJ>GmFV6`%gLv<;y-|Fk@wJzY zyb`;|AcP+64!5(U)HcIJs`;2Lf)$l94Iy&!a(l#}k|T;W`r2An$Pxw9@6Rk=I$nP| zi`b25Ic1tU+(ogfA>%KYzJhgAJ@!c})!@deh1WSxch8R0DiNWJZm`S8(d`-d>9pkW zxcVj=KT%2tM`jGbb$AOpO5@3iVs~tao|pkHM7`rqe!nrUma$PDTK|s>gkiP=ecBJn z+NlBk^$K+7!Cg@4#u~hpOzDu_z{V>?YDzQnll9YVv5v=9vN_(0jz>rQZ8X8hu9&g! zzTSvepA8n)BeR?z>tS!Ym)U8XI~;`b9M+N!W~c4VN*iplJQPWrx?5fJsFp6U=En=I zN|90rYdVL6h8-{qzZ%N4M~C^t?Cv7r*L*8S?ZwU1I+mAX3u%XKpLcjliEl}H!@+n% ziptlvYj$@XzXIkVlxG^FN1$PTSKI9;I$cCUTww>hn{Ku1$IaF{SzQ!6k*MS#LYc=o zb+l@>YA(~_tS>JW;D&9X7dIhj?)bAahCB#cAL-pRL#lIIYerUPF%-F>e@tJjhfQs8 z_QRQDZ`RYoJh5Iw=an0`;Y<~!vHZ-3G9wv1-QWbF&S|CwuT8%~RRfojyC&$pgVlBe zEq1XT%m#$JJM!f3vZoewBNuy@geGu*V=upBqU-L&FnQN@I7 znP72ygHPzXoD^iUrrwO`5xaQ;`vj#B;R5#=IfxkB5}ciW$?9rhI#x`qO1&MBLB_5Fx0%iUrEX=uTEJ z!mKf3234@UxGi_Ogy|JH=YdJ9-xxi|7)hs>m_+Vj_XgV{XZIH9iPRuW=tnST%K{s( zMi>S*MwgH1oo)i)c0mBHpZ=>ujTnp*5_x`Gf+IR0SWUE$S z{5&-3m@5%6NWovZP_Rby9QM<^x5rEqV4xU1}9HLZBS^s zjkz1%IE>OfiZUI=h8V+so4}?%it;*&D!Lm+ax?VnWYpYFrQ?Qk4lt?3kOnX4(~E`m z_ha_$TZ?n$R}r|kCiDrQDyGuYK5Y2eviOIxcIph zC=S7x|B6H$AkoqF6N?=Fu~`+J#d$RB_DUHn>gsN_Y-$6ttrg5+qphhFkBrR)5r2w{ zJ}MoeqgK`HL_1M*V9*V{snOR%J*#D!Z&ro5@)NO;Rs_a5Mk&Dn$koccU|6Qy74y(G#&L6OeqY?Mc{X zdg_`!gTCC-$YQWkYd`wuYQBVo0 zkce}r7^%s{XsF#vvtRL|f_;6UUx6w994gA5@Iq-F-Q?Ew>+Rk&h==ee_ zsbxW>^BaDAhl;v#;^_R7aFl1ZxzwVkW3HNA4u2)ilRil^JgJdBcE1JIagRc(jHqdN zk}AYRCSG0(O$!!LN9CFaqG>y3AZl)`{uf$c6OQk31KOKV(liwbfA885YxsnWujFkX z2Fd66Gt$7r=R9Rl_y)2v1h*Eo#VYqFkV)>f3!!yPA9*djwZGJ;S1pE0KUyTeINmW@ zKZ+V0o*JZ_0OcNXO|P~s7h?dZvn#xgkj+i}Ao%_dR8Iy_SwHRfhWBgaoCN(tBPDEe zd4vOf@-NO#2~ca_Q&e>F@Yyq!U31qk?4ZBRc@x?}*=EQ%5l-!>7q2LtGWJ7+Vc)O7 z8B={#b%4+ZrfzuFdZ!MP?KPiGha|!*kz7%E5`gjRC}@T{+>ms9TXsR7{iKN+T7j*JdgKv<)}UPW`6?Z_+}nxm zu;CJN;ba4t>xQaBtTw0KU!N0huYdo%Yem(ss+bMQj=X9B+t@=G6!j#@IE)2n@|z^h z7?aDKT(UK_(`>zVi<=p$itkD5$?*yCiTRQ=LrjLjm$V>apG!?mP|IFTbS1eZ|Fb}F zm>b>{dM= zS(7=`Dm#i+19XZ3xyBPu3t064^*Yca54UCOhVA|5XE<%iZt+j@+gO@-v2byyG7`fv z4yK?jljSVOCm>sB0kNmaBR7X+mhP05` zGc*UR4gvds6TJQ_Q~(@624DpM0Z;>MI-^|nlgGqfz`aR)XnK5gr)zd+cCQB?J^g%i z5o^CHg{tK#GO0I-HmTo5pOv07O2g43Qo?$W_!0SI1?CHZ=GDxj9fKALyGZZvINXvw zgFTBqn>&%k`a<{UR-@o8Ltft#KdWa3c--KV83Kad^cjb`n z)1~kS05v%wVazxzFRSXNs!_Q$WQR-z+HdgwmOox|lsar{Q&m36H9H3-N2VItlOm-g z?;+ctwy;0T_1&qIvDqKUaE&4cow%o6zJ-$FXh7Z}%Ku<=g@W-!k6Jli%WR)XACFxe zUPATxTUV;zvZWKRoL!SQ<4Gro9a%XP3B)cpWd%FmhxW(>J5Ql;^wype%02;UO znGY|LDQZ)$*EIH5dk#o8>{_1m2P?eZ&XV&ooB5lCn9H&d?r_tG_-kUYmA)n$Qe(ZI zPpFThk91%T0SOOu7^vMX_jAJU5V1Zp5Tby%*|lFQ@IoY~1W^bqhqW)0JQTgy!|78p zi_og#K#SN#@*<)`CMFWUO-%@}Xc8RHy>`fdFLsqL0IM>t|! zrenJ&5+aocvvW)tFpNj1oLYg;&M>{k^EqWrWo1pO2g2OW+#Z>PbKWbvo$_MYzkB6s zUG6$?iNAQYwdrh!#o@w3Tni^yhpgxZL;*1dJt=#LX?TG;To_1t6U1KI8?S-jZi=L0 z0a~}SCt^^zT3HzzBEdbAX*MQ;O^@~^a>C5p)SVeyxK{Fxj#aSfQv+3Sn?Rr~U>e2d z<|rsvRbezrs)`3qYAEL(uPA391x_GX{Ml$5J_ftKiCLG_?-c$*ILVt&`c-HVJAf^gDY{!IHg^00=P6$Za`T%NZo9y zZz?-_Pzc$%S5=X@am2WrP8(6ZPO;ukKNS>*4M~ncY@yTg>NPEdz&-_!zgrVe$L>aC z*|xO!vKgPaR5CJhlS~)l04u z#7RrMro1r)LbSZIFafQ>O*w$;I*v7Nq+~S!(%ZY88s1h^|8naSw47YKOqLsSQ;{Co z?7)b)xn%OQwd-%~gJ!x9VIqgfcsM-71}~pH^|%Rq9+^sG@LfI0{7scBIvyV6{;s>% zK$Dd5AsU>vgrSA4g4eceXCj&oixp?1tSP-QHZ3W$z%O9Ll9ZYlOzipY5|}HOBTm9h z%T3GMp>uUG2)y>&Mnt5T@q92o7R_FC@PThQyi~RH&bVV@3CVKNvQmDc!7KQoe^NY$ z1=2Y5V}9YKKQ`b2=GNlZRAy2gwU=QI{M{V<*jD0-V028HWO`*pc{+*i)%(y`_G$+L zyVXwjG_M)nbydNo)zji?Y!)wT;2n#x6@pZC9Rq-^lJXG^gF&eAWZ5j4oHAyC;f0yU zT#xbaQKi0=HG_${-pyFq7?p`f^t;|%qJBeB{V2~5hqRS_?Yp04co2zVGd=x*4~N&A z#0(&V%lI@MddYrMo0-1-?XG27ozdV1E+7Ryy}?=KJ$&LEdgL!Q8~x{QX4A*8cE?RQ zT!W}*yVuxb@HkL}L%AXHHJ|>({F&V3&Ra$D(E%NgP5=DM$PG32h?2?^eoN`u^T&?Y zn`@=>zQ1C%*tPtwi>>ow3=vKE(y(5{C@o4D1)^VQ8xG-L;A<369TFS`#L!8olp7=B z6qtQ%o}7qvw-Q)B^YgYwnWsq6tKX^utkCBvyc!C;vDIiObw|*DXh1kK8|jGP6MeEF zi^EgirjJ|@-&%Ht`Nbk`0M8uVP;FztNsJeNlRIcLEB&?>2Qj^4xkp>LIVKQFJD#>? zUMlZZ$+#094|O{Av~hWv_x_+d|9P^9;z&6rAgbwOW%Cn zUuTz#d5VbfB=2VS28kUszO~Z=>o~7XwW^NOGFgMopzRb9=KspQ7?v(3v-p!_%X6~A zH5zCI*NC*!6m0^#lve(Wx^5#glV&VVvi51o)&2<1G}~)Z7@d}Ietu~G;&x+S)f?Hy z!~814G`3{y<2pXh2y6Sz^xg53rZNa!HY$pliY!98VJu#fgWl_tD4-Xc$GL$wbn8eN~a68MWc_`tuClcpONmx|M;pou6GYl`!@dk0ygk%)rkBob8Z`us+P2N)=((}f_%srr zyOrzu^Yu(s2}LK<=B&ecKD7xwDzh` z-P+JX-RokzGI)t27VIC_lY%V?2$~yy!Jl8RAKw@6QLPY(3R7-c@qNUx+O|K$m58rP zK}YG#%xcq5zgj$%;l)vM8xDHB!R&gRvTuPjGq+fTN3Nktf}l3kj%$DyFvsL!WxodvPF@vOdD3a25`%x?6=Ef_#gQ1ygd83D zooVv5wdOM}H0V!XTJ~?e^YgmC!ZaKY?$2cV$fj;!aGGuuL!c|Ou_mufcbW~reUg8& zoaAAqRHr?Egjrg)%;V{X@baN=5P^j66{i-}ut@Ju(-Y}{uz~EvJ#WERRQV4u6zh?& z2Y%odm+51upKI>x#x+%%X(y+Wp|4xO!Ub~%--rF`)l;=X4K|yN#Z>4aCf-{T@E#?E z&ht&v!mn)5LFkS*%i7RwH$Wc?0nzlg=c*4{ZoBU2s>FqkkDOL3%th7s&44MT#K;UK zmPqvn!2^Bd7w#6@kLKhcO_z|X!JiHq)0iB8^W#o(T0e_na&}&hRu62ro|1KWAN!1% zvFZ8kR>S?8q>7&i4XrAUr8%G8UcD8QyFUh+b%%Oh01-XI{QPf&A#L<4R%w6xJHxQW z24y?qnpYG^1PRA4pLi~3^L`&E{^RJnI0!-T^aei zv5ul_+|G(VTA0eD-tk^;VQ_qm7CSNw2Vmm|CBOq{s6-P8FlopVXfP}D^V6!Ce$oYY zCK6_3*8ka;_K)pzkj*{`{UdS`vm(uJ7pMrU|NEQ(VVI^qQWy;ZeBo-@Vfqp6xzTI4 zs$%K1IfhB=N=GQV-W0@E3WH-|7yQ4A)hoai>%9G=YxNVD8Km6fQ?YR2e+Bi803c>mdX;pWb z<_FrG8C>t+a5s|}8%Xi%hmD$ytEQ3a>$yg43^C83vt|)M6CWOw!%-zq9q(&-g3NX? z*`j67m*f4DWY$v#HRm;I?H^Pz(Bxx$+rX%sYRtXKuR7QLWlO85dv7a~HOiVeN^5?m z;rWb8!%5n+g~4i)%Te*@Pmep%wmq+h z;cunzMi_g$3iv3_!uN5eH|MHpbsF1k0L0XwyI@--&{?X_{Z4oI*hIaq-o;+d=CzgF zmP?^rKV=S{&JJ<^O$rAw87ls1YA zG!gp{PN-zWzxdchhZXfG8a`I%rty@!yB_Oc8z@;`Tez7|dm2Z81e|eP3Y!A8xE2;@ zG&WqQ?D}zQc#yFZ?St)ee9tca)e~QP;`i{zKG?I?e!F$m^BRpjzkmB-gh5Gq0rfQ( zQ?tRQ{WUaki}3mWvY>=lLS$+sHeD|SNf#CM5O_ldlK3O~t7zJ}M2~bCu>_G7k;L3j zuQV_*@Nka^%3LG7$3KsOunp&=bgsWmh2i-g?tp2oIL@${4(Nl5Q#C0mqH6Ub$ zOJDf?317FzUg_w@yP!CGneBbtwWM_edGq1rXk-7n)4@I1)Uusdf#ohRht0-^L@Wf!ZzS3SI z6^X&C7G4@I1|&3eN77&_t|qFk-(x7z7K^#&qlmqO_}#mf$j9o}rk7>s>oP}6QuJPx z>j|9e3G^mr1#En7tjX@gizH>=2MN@}v`B!(dr3?6;l7tkY!Nq$`^DjHR(>gz>jg%X z8GJfNtLY30Uwl8s5}~Udx>4@tx_n{?goJ2!P`NZr!f}j?JYR_d zFC=j}^VAvagA3fkFM>30KFG7SJUs?hHc)09)<-GYJS|9vwm@+J5loH_31ze=cZn^Q z`z45BwzQ9TSzIDUSe^d}*}N;?pGpjuStSWYh25TlGIsHn#RD%FCLdFT*I-Hr^0T|e z!byyBAZT&r)RaJR3UZ5uOZCN2eG3A`-P8`%qumg()&@D>qQx$f`EsKF$;PcwUDw0d0Mn+}AiukE>Ilp?-hfrt(94j@r+MI}X@;-llVA2tFuv zrNLuirHQs9qEJu*1P`OA>f3u^Opr^;wciOQFu~`-I#3IITPzNhBv3g2;L8?8`o9xO zgwxb8QRIi3p6xCV5t#%XpeTPpr2UxlHPF9-?TFxIC>)?Z!xD|Zv-LG&h1J}1uceUuEx1Kim~+g2W>;b7;?(0f{omlndD#f8EWo& z-M^bS?;tjao&PXX&){zm8fe%dVCb?PP=+`KyR_|W=~;pOt~fYtui-0l{ec`|Uyou# z_{r3SdlPwJheKtE_cEXQw0o{+a}=(Jh>y-Cu0tbZqRibYRalt5E^@c~C8qcrb-OAD zz7(xWUJXxfJUh~7=mU;*CMJimiW#i!LaZ_+1sk_w>00H^)@dv}fzH_=DGHo$SlQ+dAVHddj9LXpdYW zuocq+o-66G7*vayMQ%T;Fi|@V40`4J5=q8Ix!c;qZ-9`?l>`&MhMsPE}19C=WX(0K}_id5E#7D3?6_u`mPh z^m*qwJr_&{>jn-hBGU#6QNv&hyYlj&^lY6J1{KRWDjL#rvcEb> zy1Ch9-6eMxVLj6`FFEW6q$+ur+Qor`QA8_bNWULmXv-%m%J8lQ<4+3u0}Sc01EEzu z!xXMAM5vpVMPlYu?ww%v#>En#GzLrRZqI$i6)^m=8+^mbV4jxAOda9TCRxE@hd_v3 z2`zZ}U*w>aR8-b`x!HdNNRgX?<0gGY=sXp%hRjVho7o-@~7WJst zO>k#fiqUPVkG;xE;{{Vt_mOp?uB~1)(_LPwPgwZ8wDWC0=2*d5G?wt|(uKj4L1lvP z+F%Uy2VM_4O=~85i+v8aYUjt7t^K6#e@i^5HVo zYPh2KZpROQFw*IB60G`|6Jm8k5dAU4@o0${NNxCuL#KMA!ZH0_ifl3eh|w~+sM-_` zYXM}W7knrWY%QiCFiHxyZHU9secxFzTKRlWN~N#9gi0elVWbaRpg}4SDXDd z@nFNT715wvk%||yhKnjg#WupqaJdb#wUcd7w0HDTG|Z*H$h{_Rx`;y&Cr8GPg^XR9 z3`{{eNKTHrtIDB>4}=>@^4U0j+<9T0LPn#!2Cc-rmLq5~V)aIQMntf-<$U>9wUgsX zh>z)*6Sh5NObz(fQ6eYAVzyxeBO2ES;xX=C^;n24Gv$fNpLG_~!+g>J`gM$+!$ujS zQQ<3cm6>*uZk0`5gN-pod=1W7EI?<*+eT|u6`UeWEfoTJdXWO#wL0w1U?#1b!;t!? z!-r5&4T=*%(|dp8PLmOaA_H-Ny)c{15;3>YqF8chVWE=DEF6@!e zn@q^bHR8(hakAC~`O7lvu~M=O=q#w~DFq)9WuCW2nWH(c2dS4cQDSzOy#{^BleN0C zm~_P62T)Z38_sU;_p~cCJ$j#viW|WQ=^d4B@9~DR*lga^KW$tOx1(y7SrpJb$-VPz zaWLyXw28KHcRm-NERRiODKs%LWZY`u+-{ym1Mo)u=?zSS%9yk8nAWJGe?#FavUY5p zKbpABcUp%Kfj8g+Xky5RBAgE;BhCjEoGZpnHS-0R0KcbQN}AjTM(OE7jTq_vsyS4R zHUwAF3>};?8Q3Ov@oPdIhZHJ=B?SK}V{FhDpO5b|85;f|A2zm#d_DD9oCoiv8+v;kENFDkL{o(C$ee5EPrgid;qxU=wL&S2QixkoJfx#3 zSXM+~0yY!r8flV9N=4^VJH6mYhreyYIN5YGY-E_>1GrghYf zHgmnJD{VDD*4mxsfD{QH!fx@blcOuNU|r2sukiZMCfR4v|?-m-?2y* zNpr&qGu8VtffWe~@bUn!KQ+kX*t_1Aj8$6!%Xoksyfqk5@j z_)ge$y}f%dZeDC1Y+U?h)+FL=d3`$G11cuyim{EU8*w|p=kpW`J6iqBe0~4q&lhw# zI!WPkWcvF4wD(lw=K!WFKjz_TD*K8smR?{DriQ13ezgr)drSL(Er3x zYN1-*fg0u2aX2Wh5kscP{PdS=-!2m)Iq*fAJ&{I&?5{tKGdan}GF`KSlqwNxm1SfJ z24XpaQZo!i#zsaIWF`5jJn0ijdyfRn9!&Ena^g}8D0~A~Lf7dNH}0_2%8V`$oL(Jh zcaE`s+@1PUfqNgvwpb~GT%(rU1eJl#37olcD*!nU`8JwR87z?3U**V2OWV_38Ydyo zCr{>C-q+hkRzJ(g=NBIVY)cH{WmV)WI2ihRal_6#fydgh$*w*nXbM7VsvKQ~hmA-q zT@{)N$(pIA!Pw)3SIEn(5>lCIyMfYachY3^4o1eLFMsd7U^}~9e+6zn{3&^B;;UyM zpr^~_RF%KgpR~dLI-v1h{M4j>lX;oU2)X^3ZPfH0UxpF8wh>^H{2)!qw*Qu=Z^da5xyXlCuqOK zTeM~6oeH^}5TE2d*E0~<%RhHVEywhLvp&X(dOmm*wAN5^ro4JT*t~Z*0epX%t;vwN zls7}hT?U_@Dawtzd%bD;yjl7n>TVqpIYr^M$wd?-sx1_vimRc>mMCtAVMT<};z;@> zpi}y!WV2IH!VbV9)RQ1>lxojL>o^AUWR+PLs1_4(57)9xVA*+cGHna*tqPert%Nql zCekVX_F`d=`U(*+g%oiMDMxo5|Hv^$$3UvNw1p_$=og|y#Zo|UI8q9I!M7PW7v$vXpH_yOdO_UP|ApdoQ;b{1%0oaN5XD#Tos%3bBuNu*TgW5za=+! z)=bo8;c^<)NnD~p+R)m^7 zt&wuwZO5Ag%>ot={6@C;q91%5DQB9x`k3X&7V}5_+i(`Y)sHlmd1bAZ{Y0sL1SOw_ zi?X4q?oXg6kY%7bLz6Ar$KAGt;y+g0E?Ng$mKt=adhF`+Vvl7LR2ggKTecf^18A}u z7+-sljK(9%u?SV^29zAtCW}xbJAxancpItHKHLPrZ-x!2F0q1Ph~GyREjCR%cUYy! zj5j11tr#P?$T&~gOHdC4ig{(9^-8Y42%yn};o_hZ?hfV!3(sWCK!cO6#leW$%$8FT zdT9F!_hE2k(0k{8q+1osOqklr8sMZ z4Sc6MtnzZ>fE<3ek5cMv%#O=Q2Z+h7{{?VBkH1`$KcAdE?f6Zz&N}{tEme7_zW0q8 z^zIuMeX{BJk_D$cNAAm9d+~Y1f9m;{Uv>Rbl=#Ki)^6nIG*}-q*6?$kv(v7U1oljA z5`BVcywJuSn>JTFOK~?qz1r?e@ zq!iuIFD>qn91U2T!J=6>OJjO4JJoDK5&)q}D-uu`l)67%W#XmZG?b`E^ zqfWxRe2)3tLAY%fq$fR1JH5-}k*^e9vIyxuey*v*ywf7PEza~jIpP{_ImRM6J*0$$ zjfJks##u(OA1O1|x*Ew~<4Bj}wOUQ8>QqdS7F1-b#pn!js`*7rv*|_6dc1Xkb+c8l zc95){VJ##^L(yF%8&Yf2SR?F7E(xf`AD!1jrFJ^&d%C;~dp|qX0#p{+w2L#jK7jST zh2ynrZ@=ofO=SUg*8hCwP6s z{hSQ<`=0XgQNB<;)xX9s04)n?e&FrIW=)4IjkKWwMl>=OBLbNqr1=YN8||CX53viO zph@;1XP`@RX)MhdGDIWkA#2PNssO8IU1)s|TWVldbYPM_3X=ShS}{SHgRTbdY+bY` z>0f(lo-3fNf}-nVskJ5i*!F1ri-(szHTy;aSN!@fmi=D~ z7ff%xFP?>V^xAn#9wBq>=X^H*mGkZ)!#92QV$=Ab?~3bJl9d6|+DkSkpUCk;(2H}! z)3FmHGRY-*YK{D!<2|S7B~g2&oflBW2rl04a=UF92hpUOcvZ33-0ld7IBJSxEW{F0 zh?6Vi(IycDUUH*0ax8K3OEfgi?MqxP#qDkeMOcD<2em4ogLB^JQC?V&+U5YSc+UY| z#U7NKlCB4IZ0!SGYO{zcmM+zAuhE{9MXl@63TdULL%AbTRbEAxBza_}N#t@l!nZzl zQ~FIoZ~5ZsL(d5xJFst+)BAk-^N;b@tiNi}j6vx)c>7L2W&QqHl!N-lj}gvA4$6db zlD-R~Nr~!{n!_ohJXA+Db{}USPVm+!lcF=DtNV)C1*LttYx=~GBfwmbX7m^S{=NUEm@mWA9+vD;zzo$#G;{%mRclA z+0)~Z+{v|GRaFN_bX7TT;gW3YaC+Q5THV6+dw|Q#?$flTp(NLS(WV>Uy7RNsUtN^7 z^>>j|UN~>{uBjinG8WXgw9mg}-r&>bRkhg$4z#%^)jTqJ!``=ckix5C|(s9e&C4)WWMrHQk>+ll}XSPG;<2cHjE&{49D$h?i#w`7!m^f9)RgYdxD}3 z6)-XzK-U^W!bX*p5!H*9sej;{sc)`r526BW}7y=b;GU*25?O3|$~oC}OOy0=^k z^S6y4m6LImdvb*ZJKchqeUtOIjz^s@*?#Z%n5eI_UFX;ryf5ZH=WlK5a$}=*j&W{4 zoAG?7TEH>d7-NpsWvtMuofPm|gE^Z^N+u*ZnNku<=7Isba)TqIq8xU~D$xuH+wGDm zAixo5wOVP06$b`dkRXbvP`W3O}@?2+cE zc_6OsW;?Od96KWBz{Ntsk4#jzHYHHaMH0waimMykj zhpq^mS~TmP)4|8z*W1{!xV7cHzRcd?2J%Q_Tz1j2n|?h!OkSw6 zXS%5YW$Se{V5z!8XFH zIbyT1zH*xjdRSj6LfQ-&eP*o4YVHUgaa&-1u1?h334_m~Rd|p!ikoj;5Y!me zXzvbMYFC%4@PK)>bwk+tsDqp;!p>`8Qlm3T$eo&=jpCz2C8RNHdNiD-KPKMeH=7yT zPy{tXUyuXL0?m0c8iX0B>?brkyV_MV)KC`e%_Y)KdiJ3^w} zv2@lfSS+B*FJh9FzBhxa1r(D=#VLvrpPS-mYGK)wYWlgFrUJJK!qbYbmM6*G@D zlee*L9A;U;M5&3xqpU*MYtx)!O!;i7Andxs-gIUCwa;|jd|TtA9cv%3g!awY``86b z%U64*j&!IVzN&e~J1^co^5yL{PYgfGRjo-Ysl!Vi+;(Nv<*xuAbpV}L0-fo3U|+a( zLwdX+bbHv=NRDn#A27<>)$BF4u;59kz*vhGq7_ITLq&Y5E1QPyDqBRhIx0A7t`Y1f zv)O>G`fy@2nH*+=6_RxFXiPMy>Fm)=_soof$u!R_^cY~c0x`{IbFsPByu=Khh+Eud z!2+{Mb9AD?Y_^%BMVMB`BY*nVz|4tSv7>bTxgI$^W(iXRerQEa|uDJ-C&);;_l)7sM6ibv_2$VIUZ`~UNZmV#m_Huz&8-fkDYoF#n<$vJR z@g$MY$GM~uufyB1#%Lm(jsV>h^K+h7ty37&B&&Cz8*&@X*=)Blfc9AiU6_E`#8T7) zivBB*SF+^dP@|b?`{y3QMShz%P0WPsE2RfaAm~GDw4%g~Hqg_CU|D&m+E}25dh72G=AMLI579~&Lx$k+3dUdKg(4FDmJZLz`ifhZQ^q%eD+MrBXZMM`e4 z=m)TqPT>VSPmKy(3!egb5X7buZDeZtBXdIVM%V?rB6sDzG;4p<0~>5{$wgOQWywh_cbrE$F2SmE zN6x!s_)t%<6a)Be1io6$$Af+!aSw)SLxrn#hc$=AkAwacq7k%$uvxz~kLYF@q#Ru6qt9%(CT8QIodH$TsxR+|4vt)U=+qsLow&5E z^jE|)JbzAlQR(#a8uNDG=U|G0(XC*+tpl2h=mz(UfCNs`SBP$XoqoIKB4MNc*Z6kv zK}=uD6@&Q%F(0X5((IC7*#+FR#3ip=%bT`p5$4qMkk+gvK#d?U+t}*rSD5R@yD-6C z-4LG1HUsF88vbk3*k&yiJ6gZVF4LSK+74>P09!UrnJ;e-vh5FCx9|Rirr_*ZxaHB| zoB8H?2ske9)_Gs%q&cKuq_s<$AiO^xgj09Mci+cMpa|WNXC`7IzClwrmZh zU7j*cqdRaQdMN!I3GoS-!!M5f?U@Bxi8$+>k*Qe!%!-(lkz*vB=pJ6tj4vAgc=#_T z%G*ZHr54i2EdE@esTE!8o-GAu=-hgD@HSB;=yir*!6qi_@(pUORxlWi8ia#TAl7mw zv)QFFS~VJ@!6<6XoIwlbf+z}Vt;k7%)M_wd_&1941Uy8pKtVv9OMo~Zrk_p+am%)b zlx=+FLtWq6m23mB95a7RTf|t!lBcX&F*X4tseIMt=j6pWvkM>CUl(G*Cm$YOxa``N z;*qwy%%RZ>+xev7k8iq(oBQL;`}?TYIv0#pGyA6O6}PQ{wBlCMi+hO*si?`>fH$gC zD&-Xm7Dh%X?`((3p*2{3cRSW#${shPSNqTCLEFDabrC#};LP@aXMp-gWb5m1Qo-$qrvf4X!q{fM3YhAPv}e zHe^Q$V0Yz#+G0dwa#~t^OdCo+^+G}V3&&reyKL#{>E`1vm|uVq0I?Tn)ZejkI9(3S zFUsX76#iLxprq9iLr>UEP{KeZv?-zQzGgxi{|EOcrsF2m?`w~HEh9TS<0|U?&6VlO zjHR)Wye#;;*Oi&+BK6cC4x<_5!qKwB`!ce#vRGmK481q9i28q{g|DJ9ZZ~)tkQ#Z@ z<;r{)68IX3G}6j8fQKK>O3xXtgxJ*?8QDZq3XNzW@(F$H57`;nX%IobDD zy;fdt)mzOZAfO0(qPInpxsl5w$(o355oB%XMWJL~Pjk#ho*z7ikz1R>SX$@f#Sn>}rQyhp)x7-vuGg`A9XyS-TBRSbq2$v;)?-|2h z2_Q$GKT)^)^(~$oItyyAe$%*Q*V2ssbC+DUG;@HT{KnIljXd0yx8nNNox=}z{WN9H zl~49=zO>;A@OQDZ!4RJZaHpWT?&QJ{td0wgBgt_%s#uqT&?0rGny7U;oElUd9E{^o za8OXPSyF>>_`reWVx*_w768rDU6ukMwtU=5L6a-pWu`p=@~tU4dJ2~}h0DpNFCiKU z2)I~R7X4_^$ZL$8CYt-zPfPXrJqNa)bA4l!En{YGPD_J3;9xH1pI&-tQ>tTf{gUOb zCFj+p=*UgE>o%;Om?LN#TR8I2YS6|{zxmI z&ril06ybddbiHmrMuCjFIXMXKD<=^UH3~#Dt$ec&=^&Q&eYdV=zg9)pvffeMsd^O^ zqFLx}_wtTfym>laJsl6m;N>xRbu?ZViQ6M_egsYmzy)TUY{qC>CeF;wjj9W=&YENk zsndp3hRh4$Lh8U2ud6f6j!lk>o0e=hU_)Fi*zTFu)Jbl)XirV%>T;`7!5*b=SrxlZ zXJr*L`VMDhSzIq>VPFe|%)s9kSJS!Mj)4uOl0V^Ua*eSJ{fl-`bDF)m0Bpmvm!5OS zdV(HsBY#uO8987i@HIwNkTr_1RWg-yO2=ia+I*|$)swcuWmmtj{gDq+1zps%zJ?`N z_BBWkq^!R6jZ4mXpf|^C%C|;Y1yf6W_vS0B=bt|}CqjrWUo~Ul;p@At>19%Ed`)q} zZ|^PYZCg;4Yq7b^sdbKmc~d0EjKTVp?%P&or!TsAVeYw=BY$7Ab7ifRR8HQ|hlYmg z49trl*mMV(}IUua)h*hBEn1iUa|LjtKvz=;Vs0ijT9a*{JKEHasXnTQgSqQk;e z>8U0zCRHV{fk@vIlWCrnmF{95CF^)1H(^R(Ixqu9S(|90=>ba$A#rrBZ^coyBW?Ab zC(qe@@`ib9U&Z!2x4d|16(^YVKR&EdP3pX6;hgQg3*vt|%x&lS!v_}Lb$KtodFic# zQ|qohwd-j->#sYic06@%Xl`jkp1m;KbA3l{`uy|ia7R>r*rA6ftvN&`JpH=ib@qj4 zue&5llA;i?B9cd@5&EkF#u$rEZ!ps@ZHy+X(Nt+#Ya&Ue0u!;Bu*n!;u@Fsal#Mf+ zh-5%U;K5_ZjujuHj3~=fz;|1U^7V@9m<8B91TweDW?6LLD54t9fn6GxAB*u*4;TV& zzGl;r^Q(jJdJN|uPSYl@y5NeMWxrZI)nGi#O)pwCdE45#mp{7vr*~CDZ@T7|#3e7e zzSq@1^(T7TF{lV33-FqZ9&~r)>2XSGYE)W=H7z3}Ee#nBfdMA^UcA|2HCt9&aHRz& zS-@(;7PA@Iqvl3!iy~2zCQp{B(N6Gug7fH?98mM5jJ(v8sMy$0YhWM|QbR)nEF?7z znaq>v7(DjcG4pFc0vL5!Y{;d%C?^0@jM4&UHPIU(GIz-2p%WVmrg__Z+eI>Ul)>ju z9-BXz9OFuHKOQieW6Rs74&J?Xlxzu|=UrMkb5qMyy*Brpi>{hE?~3IGM$_TP4j3)5 z6>X)1chREU^pf7}^5)4A<0xt`@2)HDtWHc?boHv7_0xWWV(!6$(#4a)5Okg3^ z&`*aICVHfui~ga)pNTlShUfhpA#NZ0g16e8fOI+$69O}2j_0EU(F*h86c7ahOo;_v zh*oPkF&czU@$fPDr}~Xb+-(e4*0ovbj2{5-XpMt&X`=I7fesu)E{Xf_^oMc7gK>X4 z{gG;D-;cNM+s7|vUtld|UtopMvq|0eHYVe^WE`A~=f>l$aX2Avc^ugiiyLEcN;D3O z#@c8+JsgLG`~)M9Ns{1A@H??w})bl zagC9L8ci9djO2^b@M_Ce3rVw>!H`afYKLx1HkpLv?x^!&P()=VB-3TYjP$fMp?FRx z4t1tlP3G+u+-|}1VE`Gef+LmZs6cd420wMoJe<|kly&MD6^zVK$x1)nL{$=1M$m^L zr_D;tx3cpKe{_2$+9xps9SL-Hn?_VVYk?l(Kt(9bet-MNKjN~BKD=#VX5;Ge-@UP= z>bhAMzW3|J+)G)rCq+-~yMDn(_upUCd#lIO{OB8`4R8PYFHQIS}bZioxfVJ>Vb31o_uoTaMN!ts7|9Vy2$UQt>&j7D@sO}xEHR8$E!oOhLE`- z%R|T(6HYMUi}l#4$D$tVbm6*-bzG8&bz-s2HDPw5V@d_2=_uuzT<5Y^4*f^K*k7DlG5C1-brlCzG>Rf0c+R}% zuIdXG=Ok6N^q=Q`$`g0+-8)7y0$q(|(>FEeJBsII7A>nOh!5Dazo6^Z70Vvjy>?FO zqj&uH7QQ*HeQsgXo+sOjTgu~H4V?ph^dxleHzb~YlT_|b5JZt0r@>ZhaIkfHFb+mm zOwYag%F0=7!7$-h+pVGjFk;e>VbnW1HnKw4pio9KJ?2V1I)+`iP%B2K1SHvX$qOP1 zW;g?a_3t0VEsiOX(a9=quTF@}o|crex{!}yyFcx0_a_9o(SCPd`Q*XLTPAbK1$b`o z@?c^M!h_Z=Rx;m=3(a`Gh$}_BL%L~&CNt*Ojl|54z6K1zN;!Q}g!CWqIV;u(rL6Kc! z)|ZQ6J>E3%nE<0Qoj)U0@_ao_l&zi!$K=@=iF4|vg@r}L7B^%RHrEs-SV_kO(%JMu z*YBLZaY^31D<1Z+O(48?T4!ZkOu475{DP8_<(2W7vpWX|N5l!_J+E-tWd&WgwD11m zKuc%$@8}8mFcglfQb95{x>H3#&}lVD1F}(rkOQl&7^~N+u^RInM_+s)I;Tpb)~eJ9 zEGNp*?|@XG)bW>*=3~dqR1V05iFUBHau;1I$C>+&Js$aZ{{H+&A|K)QT#U6N-(O61 zPyEPSwicd+u5-^V%)oP_a0UEFHb#=lU>pHm8JcMg4Y8rD%+R?YtGY6=84T@AZFZ8B z7#FA284M1sXw{0Myk47`YLAdiVWB!vos?*>*tLi#Jo{o6Gi@M7p_bJWgOnN9O-wl% zS@%vHoxWi?woc8?(Sp>NsB{wFPh}!oyRNJ(>{>kCW{X&Q=np|N&N;AQ#+H^s zp1XT#OG)p7sR56jh?_d!RelRy&~8jCDow8LD9+!GpZoFqWp}JAk1k(6EilSr4=Ghq3;Zs^Jdu&55o>so8r6^`f zqpS9!)`FPgMY~q)S<{@AHr2Ic%dFKe?_F5bb^Vf>E3Ule*AI3qRTUSlyn6nmd4-YL zOSd-^wp1qlH|zEP3neGaVU_paU`|_2=dj*~fLD_EUZjCJ?hbdU$rvcYoG*|M^=hZQSP>9{vVDGxx%I&U1(N z{iSF{ZJJ|JUFr|}aKPfVWuak(ODnvH%n4uLGtM>Ix|^&xm`0N!H3>kN1$ z{Ohq*3JL;wkz`IHNl7Vo35jNrh$zV#lmu67nB7Fzc4F-+oq~Tsdb)*8{S;e|t)`uE z`YR8AnPXx?Yx}ail5_U=T{%3oB6I$R=~8M9Xa_yA>JduU7wd`M z;M9wuP6R!!th+Uho1ctB6f2#p<{cmoMmrdn;BD(3-#TsH10&zxH{yQ#PdlF2P~dFX zPES@fcg4i7Q<`!4(b6=sWF-`@>Iz-*8{lB5qgX`S7p8aj~(HF#%Fwpd%g(lo*EYnB&ycSlyo@i_xu+08)KR-jv|_Pe#QAi63Y&o& zu*$$`HN1xB9ZYEVK?lhj@>;=3I8LP#sbIc;oM1jiMYAH8nC=u)zdt2v*pZqYS&> zXC30PhFB{utc8kM&%=o3mO)H~irir;_SFSw8obKqh6k`Y_ps^f zp_f4D^!}8tj5gI9Pk#mVhZkM!OAz5{3Id z?%XEZrTFJ5+1kbq&Oy5~FgV!e%B3$E@PFF(n0om0i+A$J_I>^L0YQyPK(+%P`hmOn zr$KJ-PpzO}gR_@O_T@$7luugy$hdO9yDcB%xw_>#vIXHa2zwCr@hBEoi(5r9m0Aor z8l0fPK^m|jFo-u7!B#@B1^3c#lQ3mq!uhy&+9>xDPAhQ2FLG~zqJk!n#b-4&G4o;~ z37VoA1p?{WIWAs2II@gCeE-+`@s0f9{oI0|ZU-K@om)Wd=ndY}s$h^lK_~@naxYqu zygr#MOvVe6RwV66;^xQV1_^uYgZ3?UuECDyM0uhJOg=jj_(WF|W?u1SY;F3+gc$(q9ZmH??54OFaZpPD7- zS8QmXU0_e0y=MBNyEfH0>(_qP^U&7m&e?0OexP;D(M=T{4}P~TrF!|AO(ioo%t@`i z^ml_5>lb8mKb4(#pnK81dp0g|Rb6+>Fpz*EF8jlKiz&~LdBATPx#)fd21``FIiqV(nzNFc4kkFH_Cl=8|$k` zGzmTGKF?E#m*?Q%Y`h`^uaCex!f{0b&MUwRbMX9ZT$PUJaCoZ)Zx%7gQ4cU^q9vL) zfcD5sH5v>mi7o;3q;jcwQg`UCP-2ETWt=@WHYhPJu{?23BA*x?Ww#pa#&s6F!h#oC z@N^5NhnI_1U6$RqW9MRP!A+-5o57-Q%1W1emo1FBm{w5+E&H~R852t2Tl-ReqhV|! z#ewaw{F=kq-WlZuE|T_i^pyDp&V^}p<*Qv&ubidyctC1HyuBo*baGov!ju%7A9wH< z(vGj)y?tFqbh_n2TUmw_<70rI_&|#_OuNOB5t*=LMR&eG!ytt_UIp$52J8Afck#N= z?V*ID^MW8N3JMGo5Hd;Ta2y^kMr&<$!7d06QD+r(x^%G^EXyEda|Q$ka#*Vs?M@vX z+*IHxgPSVjfC3oXr!8l)vccG-A9Rf_pgUla;Gjb~T0ZEX(oH!B*AdO1|N50-Sw%B4 zZ|xZRh8Q0H{fTFBabx`OukoatZtq$cq}al|7f5-7)r1pV{3cZ}And#Rw|;nnq{gO~6YKr8}$>wxaNu>1~VRd^^So z>{x5rU`>Eoma@vkZNuRO3*1vWW;!FMu9#WBV_9D5#-TME@9)Y{A5`6Rc}4fk#PHJo zxlNnrB+lG?-?Ha^uj1@kGjqa2oJDcb+0IyVfOA@VbCj+qY3YEc-2&5psoZIZ1@U<0tfIz~1eKXQEJ{21;L?&Y7-xtDLIdTSH(=SgTY z8nw97DkIq849X@&l+h7ru{g{|tJ!SSnz6aoxXVc752T|diy0b!Fe)(2Zp50gN%}Dv z3`OB6+g_uLiH)kUSvu#p^9OpTR%g$yS$$jA`kNNT8g&;Q*`hNB<;*Kxe&H;q`QoGa z-JJgJmKn~Rh3m_*mS>ap;hh-^5>qN%5t$3lttOXIT($!JP5~|^nA=Vkb%tOy)~InF zKaD3aId;)=Si$lm29wD}U(TWi)Mku5cn}PyVlc?m2A#+g6Hk8z;Qsye44UF`mIkQK z^f5Ddn@@vU*uglUL0y5sOU!=YPYt`DdS&QhPQ?*oa@g=+_}(9PfgXJ-C?-1E>L`on z3X%NwrTS5TI1WAM9-P7BP_ULF!XjLR2U~!T3o8#JVWDg3&z8iwloKuJYl}J?SWTfe zmrk(ibRbyp7L42QYD^O7ilZ2c+X)UO`UJDl=rZW72EDHLcy2rT%?cMjs>#aAg@iB>5(tk&x)XnLLt6fnHKT!c@8x@K@qGf zHQm5tr@90~9;1-@(cw4ls)dOYuXfl*$`2PmUWI8r<=j>a@B8+w@fN7G?Q{sV5;=xAO>O~%_IHMY={A9yepZb_KD`X-kTQGae4uiB{9c5#D{O94lZ9{D%PrP`$ zRT2XWv+y*af6s^=zfUe3xjd!X5jia%ukw1S_9Jzwk4e0k$bO^$t%J2F*eznj^MtrD z<{u*a<=?i-fzqkoFUh^+E36UTgr~P4;Q$oW`QADKjwZL0FQNCo^7alpkY+zZfCsx5 z=KYG?K)!}2-@}W&TiKJ+=#x9ivz$fs9J~7pS<3E)alX5ElIOTY)$^>?Pe?PnE6R6a zhBm|d9zPE>n+cVm+2~#O1=}UOAZoKmxxB%mHJbfkI ziJ5L*VCJ;u5Hm3c<*uX0{p956rrQ(j+w8R< z6=wSCP*YO?^PA|~Q0yH*$e>!0X0o@7vgEgnm>#qg$XktZ>`fL~`r5`8rt;_q3%=!P zwFYb|y0WT?;;7lhQ41^Hr#Q(o$14}*28E?hO^lshUc5SPc5{FE%=HbaS&O$e#Kz|@ zIe*%e){6L>ZoJ*>h>j5V#y4k}QeD}$jmh&z@@lezCuKwyW<>GE@E>r3qokxHCOS7h zD8Qb)*q)adk~E{;J+*srts}2Dt8`^mf>bawZSfsdp9N*QvTT#8w`Bz+Cd8UHN6sSW zM!K@HB9dK}=uAl;nV7>~*()5q>QOze$ESfWtXwU31@{B@1D^|5EnH9VPpB%#UGJ&I zan}Y-yyi}Ag7!ASBHS-d60c;}yZT&%$#A97XnftY!(3(FXWnNCwhRZnAJ{kkvc_89 z3Yr=8dhn{??`*GxYz@t3*W+P#gja+gj#v?Ci@YytWz~;1!d!4<`UT3ef*V*grb@n=YoxRRpXRrV6%V9Zt{g+;$XRrUKt|dRa9{$&^ z*ig7OkGrml-3!-kQp<8 z&JE7*Q+rciNefGBN;{RFoxXrw+cT0f_GX4>_GKk!-Qb$x+T!{wyDaU7y4CW3Ko=>iY8Rb@n=Y{qMODJGTM94gO5L5vfrP;!v#jAoxYzlPDH( z5RXN9-gfW{yt}|JWcfubXFiKB@_vDmpyWyLO(WlfZ()8clu2j#>8yryRzo_}a1i_= zZyETD;I50c;$qLb*t0J7Yz{ni68r+@7c##HO6DOGJe!9s%#VbUd8~XM%7QX^sEp-T zFu#)d)c|20s%P;ztYjmL&tvgL;1@uh)VDA{66z^{I(LCz1}zpqJ=Cve`SmP5hsEbH zpJGu6H5>%r!u(j4Q^x#C=2IAo7&VF*{6!4@B53O%__VDeDBli#C5uNzg6Ni{}UG1 z()>uP5{LXqEsN6~`Y0|!iOckt<1+o_xJ-XJF4JF*%k-DyGX3Sa1}%$}l(;8HZdKwuib&bb;zr2VraYp=d1Ou5&*Jo8 zcVo&cN*wA>d5XmYS^noroaVpB;#NNzTK#AkM9VwXN}QJefyIMazNEw<-zn2Ml-A=c zRN_#NGmXW=S^0V;PRm!Yc%)yuk$&ykSv{RfoYu3H#ba4Lo0T}NXDy2-v-}&CIL*I; z#Zy^5N0d0NhxM=4kN#Re`fL60()!`0_badWE3fzK7rn1veuE^Gg)&ekNHhsGpmyYe za3<=T&9-Y4kPaC6!gr-7z z8Qh_{&5RP`ag^)nRxp)VD}!*ih2cw6ZBX|bc(#w_3___^MmY)cef?1dFj4Asu;;oM zZ6~qkJ*R}N-V!0awz?JMtg7!4|;z9py+@n`+*82tfY*Q4~u37 z6Xk$b)*^*@CBv@m-{PK$B@4&3N_l8G(4&*JBLNKt*d3YviSTp>!>4<1_b=bb zDB3@Ybsn@%xqhsC>bUYV0h|A`V?$)_?D+VfGCqFJ2V=cd#yDXOln18!{hy^ylM%;ynnk^e`-C zeA^hj1FZk%Ga5^51g~Q>m1!_A+ONLSG_RX+7o|Rh$;04lW#xL5{&zCj(8b#6VNlD@ zwkWlDlyozzyN6-03Q7&Id-SPg3=Us^PVAcjg+^%!9orxI8J+_9?sfCT4Gtq2~f@udEQ=M$|_(u9f+S6w`Su7FCCynFP zGuJM|@bA*retm>lpvgvmU#c z#AyCSOl96{9>-Pk$nREs87nzXWl)bIi72$IM@O7o4=qXM-9O?{S!#DFJu_CjZ-jIx zRPJN4v4f4_0i|!LedE&-ZEQ4mGAw;m9?#WI)-%m4-m37y&n4Q#k&wtFJH@jQ=E3Rg zP8w@tC6iko)~n5sM=7)%O8D-jD|IfJASIKO(K{wB`bR0|gXUk+L;s}Qmm((AQSGZk ziX7$U6_6+QlaFgWOlNi~`eKZ`|B){9apf=TGum@?qa&x^&rXp0Tjm0f($;du%Uw#p zI~mS>ik^{W50wnfjQ(;z`}kPqs~$yy%PmknAnTQ`QBG+_WBPoeJo_)`i%|+SGu*os zo#T@qtt@8{Xd{o2=5gAQj%b-@5`8fKa^E7V$Hr-N=YGg{2a%BP#=;Q z#}6=lDwjU9baPISZa&&ie40{CmZ$!B`{3#uXW7X(jA|+T=c($i;bkoSHO+XQ z9;3t>M#okEWadI8?;MYKUK9g}3NMdQ4ngQi&pf37sIhx1XD2Li+vy%0!?wOEV4Pm)bjy_Qe zIddRQqWWbRQ5c@kz!lTQe#-j zJypuEp;VMImU_S1#?x*tyFXS+#;-*2OBv;ASxWAWQkH69eMRp& zmA#9$44okxIM!db<0X2RgdD(xj5k zPN}|QdHX=WRPX8c^sV-^rbz}vrDs{6XN^?bmD4CI=h#5v`8)8J?r}D zGZL*n!zIPjAkQh)H+S~5OO?%CE!{0EA$NLrdzVx>*xFB9YH081mpc9HY3uHjrgkjr z>}YB3l#~{sOgA(t^>+{UwRj+C8(7oa=aB}xT0MQz0L7!KL8|U(@pScjCQ1DskK|dk z%+uQHX_Y$VT&dO5-_qC7LlIHg9AOl z#H}8RCN0zM>FgO#Q5aZV>y+ZOL!d&SPkYC*4uCn$V4y75*4^3J%~(JowNqNw+z+sJ zca1W-uM-m62L^fy)6+d&X=^%GcJz2!JDStF`7=w8Ma{S+`-&6&sW zna18LN{MP(;#ErK72SXnC5>mbrxV5{Blq}GPDwd#oEr>vv~&8|AOO691Ri*5d0#V7 zrqwC6^}(10?rCXn?pqExQyK%Uphw|xse2iW)h{K>=#+@9&0@L;0h% zyJc_{^hmQT4mvu4GKsXtao9?;6+!cA5`(DKLnVdW4`(bVt?3wOr`dk&=2X~?LhrlX z*#Ueiw@vHnlSM7Gzy=Y;&?&9zZtZBJp@$KqXAltTZ)XD*s$Moo2X8;kRJa5%Ob5jK zJs`WG2HMvOmCgvNJm8^8dC(}7Wc??tsQLO70N8$ysUe*$4_5?{G&sL!9xdG&lsmDxBA9N;t!k*Oozoye ziTaY7hWS!$g;Y{AUz$->Q|6S)=hfAh&z>#S)=O10>#D2DA+M^Yw0cfiRn0VMDm+(H z3u>tfMj}+&P%F^}m8z=BXVc1Nme-e7Lb7CPRdrRve5X`V)lft0tbj^Oq`H#&hN{vz z)g|>(-JJTm+S%pMd>K?*Q&m$@53Q8XEU#%ugH|C=DsO~@G`q5-x|%grG6$fqXYiHQ z*3GZ4npW8$Rn}IQl|$y#a)7L4YIV8X6d+YvT~amEDV3GXESXl$o~ngf>RB-by1A9* zEDPEzf&bEms@fWgMrmzLLp>y&fMtEd=%aJ1W|upqlKQIIlpq!LwNO1JCp=NhDuL%} z%H>)psiko}0!3(g&g}9rSjx&vs-dpg^kM(P<9DD+*({LGp;kkV|DBcW&U$qE7w*W< z_~*~?%kO5hzd`@|+|Ar?xqG+++(X<0kU3#CERe%>C4J&WHEITlK5Gdv{l@TD7Zoty+6^|NFeR z2i(^K@;mStu5ZiNQuA)U;h<1|RIocnP&ubif0DRA<)(CQW96fz_RUiB;hRZ!_B*@- z$Jf}-cWj$(xP^oui&^#hId{9j8|ezv+o=Ngj)#hzs{O04q;hO!Eb^|rCzome;jNDC zw|m4J_Z-fs+22m;b0sbZ3lHNph^X!?ziXjD3zl!*ze;O9RHV6oZy) z#dd#okX$}3y%eJ6NE5lIfC&&8zv$MGO3C((`bU3y?;~USO3Iwq*0QpqxJ>U7k(5P} zfS-ukC&@AjXX(i_h!SjwJ_~*LxbTm>r1YZn2XDdL<#HnqU)IoQ)2w3=^@q8OH3 z1--&ub)y6-zDq_0k{E?Sl2xHlS3wevJb?+(WkH~i%!c3|U=?E?5ET<15DxjH^c!-6 z-9@@(lGXjG{u9o6>57uZC z;m%wKd2hYK-jv$~gpKJI0XwHfPYVff6@;y^hqdVQfpMU_`s7~n^RItF`ozYy_G)B> z8z-{cpk3I3OQzlA9IZBG?l!>dY z;{!A%8u<`7E*$=-r{nk6KV?0>vG_7L-j(6%T*#l<9si)NO7uRCtkdbTq4Z!C%zYD| zVKOZhfo8z_8qT_z&MkZo^XC^81**rv-7X5@k@6Zjlynw>U_{mZfPo~(-y@6}QsRyg zO8x;iCG`Vulmt3P-1!I5Wwcn9I7KN&(z<2Clbkn175$fqRtRTl?SlO&2~UK9C~=7+ zOJY4YmgOPqR*>!;$WC12kmN1fEAKtPaFFZX{v(pM{+wV%KL@p58NXb9Sz&S23fs23 zRny9GMS#*OT(4Nf3WF0~6Se~uI^1-~b|`YFgN#XMREtHI9vy!~!&zvH-hgt4iU=c- zG4?*PToCAJH^sdDu0&eYQF`m??2pp~lr9#5SBqeeBcBhXsvR zbhnZCQ;$|;LW#tp%7xfdL^nA9us)@NuTp{?Qd;8@w1Uzw-!vtfz)G<9{OPn?SipyT z9a@9>5cc0&M#^u-^1g0^)ZuPS`sCGwx4F~wh{bRIRNEtz(b@S%-FtWy^TM*=&;L{CU_5#RXG}PFwshKMu2oAHO0D z&TZb|#X545CH-#hzPB0m)3 z(ec}dSB?+PFO;0 zYrFu!PqlaiR@~=PI9WDO>GiQ5`S0z~gUhQ&IU23sXTlI}CB|kRPzq^RonqEk=JVzv z{T3JSQOfEdpft7^8AAb7+V4abOdb}b$$O-ROiW}<8BSzNyJY};h$T}L(!Lea9v;ub z`4v*q6_YgyXx=N*ITdQ$euxy$XHLBW?@<9CGRf$MbcetTuA|#rLYbXZ zX9d7Vt;(*UVCL`*W9o1}CT;Q3*Ne>8ZtPg%pZ;&>k4{3yyrhPLr02DnBHYNe_wC9v z$Fa)ZbV2-R9Ev6agR-#5asJTY#=vKU3`w46YkQwxoOFaB?WzdtlG;^DT`k(Mk!iPd zfTM$Sr(`q(=ZK~#z+V~#?-941m)~ZmrQasKywnXrA(9<&A<~kflp{f5ySTiRA*%qP3Y!2S@blH}b?5GE zBIfRQ%lN-=PLcq}mv^<39}koSe{}Ij+|vg}U%KHSu_ms%!%D18uTRr4!A~`0xJi)+ zX7wtlHJG)wnST9FNgT93^E=Y>*vA`!P9jv|Mg3Y|jh9pbiNboO-{UaxwB+3{QGMwd zD;-O+I7_=#-y=rst@v|ne2!}P7je9CiMWES0S=p{tGS_wSI1=j?Qd`oCmBDcRi=@q z4W_S@$n`ST=qu?9p+}$z5WUEL(%)7OD1s&EfY&)fGr$0uUwig?2R$JJVO|n8@$54< z9Kvp;1{RZElNv>cVRi8sBe=pGOFV~PB8ZW=@su&{{YZXI4!h*0F;4y4Hc*MyJgA>>E3vS&ERD^6V75?3z>BkZc_l3KE>^*q|&4aED2w{3T{^T zL70&N48I9-8$##VzzybT3B_u&a)Iqs^(U;i-2fFq%y1K-8BZow&-d~{Slc!Y^gbw?@bM) zht`60plp#j$?rLYRTl5|Fr7mp&F(Gwpk35ep06clWyPhBen?9e$Um zm7^ccF-d4vEO*eS{r?~v^!dNs(RO-rbZ+l8fO;~dbs=pL-Tbk_gt^^%?J)Nx>s!bqsN^3wyYNDy|R5aLu3?2#JOXH$?` zJjNZp^i7)3UqFTEFs@}2bB+h5mA7i4AwS=cV%Y(kw=1eZS4@#12l=rX3{hm@6P`U( z!;{6g8_$l?=O7uGJV#WEX^Dv9vLJltTXC92;a`bX{wqx9KN-=N3*;vBChn^4#v9l=Eb!iMnJi=};~-@ekrmjpOfDzsb))4bD^Hm~Rl359kkmAKcb+ zvxS;e;aUYMkhp6bqp*#RR< z@KY2TXUQtT(W4&a?d=|YA0WKN0x3v8Aidcgi6s4m86mE0ISY&-8w^@z9I(dTYlU~( zg!~;ySoIsp%^vz!J9t936i6#9pCkzRX?!XL0fCg`fG_sr3|tDt;R)7W_Md@m#B0)1 z#TBdfuS1@svy0*HTqky56-QIX_w6Kz*SUn%x5&V^>HnkZOk&{8^mHvz`Uf{JH>@6C zCxsKuPGa#h{(p?&<04RhGvFW?i3R{0A8t>%?=H#8duIssPe~PH3B(@XZ&HHYNzIm!Pd* zI21=>*BO9!>AiDmT+MNz)A5Z7KH;&?;7RV<7gVZ~%Yf!NP;a#zN|7uGW7aUW=g;P} z$3VOSBbR4zLBKnxoL}`(IhrPPTMzjE2aX=_f7ti`=eO?*je4buOIqGh6xRKk>(=xm z+9K}tJ!7m{gWNh2>Ay3GxxP1`6*%ja@muxn_S))J^FtH?Zb`!^3V*%t6(QZpS_vYZ z1K`R9F$kF?SUZQ)fe2XF)F=UP*YnI|0-?9AxO8{$PKKZ!I5NF46iJ22 zAchnJFHi$7wEqh>?9X1fpZOr`29W<@B!O`4?nFJ%3wce^*)iA)evR?_jxp>e*q8Vq zFpNhaj4S73S%FlFS0e!bK#Se6$4nmG~C43b-J2G^X7c5_vRHl~sOV$KRaWYZWs^Qb`0 zvGZqw9_B=(6Xoa;&FN}SBSaUn0p8|4e+`oPlj(T}TA-$OhFIWahu4#8*bL2rDs_?~ zTTZ16Rdob|MO=wJ)=q{- zTuiJim`oOx#ypvfteIXGHRZ*-?_}tqs- zsjDOP$Z**m`p&~+HAUQj%(h2t)hoL0F2xslJ21PUpZw~1(OqzmZiH88Z5N$@0uWeq zK^meNEgR`(_DQgZ&b73fY^dIHb^5bnl~RZY|EY*CAagCJRYK_e=RoEW5CT zk#HA~>GIBVc}RH%90V4eI*)Ycx;Pmf&RH7@u6NJTt_yL zH8x)Q#=>&MJL|Yu8k=yVMTL zL}aopl3&W+r3>1PUb@HO87%Mq1x5rir%zl@p9oW?_%g-5Z(Pc-?8Ol4z`PUay)MVy z0|S}A?*H&w+t%pydZcw*XTDkx;5BonT8dH>8fwk;+}0jQzD4J#O^3U&nvaq?YN8&e{XU zU-~7$tZ60jvi*rX_V81x4jCYKXIT2WIPi)|7ksQL1JWE(f^cIKy9zkc+p`P^4fYKqtR;>&0n-c6VLw6h8uN_GxY3AfT(EAr@H-D6Z*4$RV{MMraOD^lzU!3Vi zc!T5+92u4R_WdX1vqaRhM3nVJ9Uq+{Po8Dkn(%`KKf}xT#moD{Wdez${bK&vl%3%+ zP6el?ZEE;ckn13|n#Jn7@~fLhD_@9a#fU$XqzyFcwq>sjF;{qe-^V>2N_nd@TjMtP zj`t1D^$Fd#By_U=Delx}`sUqlN3aqG7c@lQ?}};6l<_=qYu|d@3pnKF?5NkX)oDCX zF11SIORo~jYkcLY_n}8MknB)|c+G!v>dyZ(FCDLM2i|$zFw^c>aOr2J$<;8_{QYJH z5)g^eBe!G_KRXU#2aRC9k>D|Zk|FC|AK!E zEDv;awFg8CJV+IKm(BAnSrVPGB|c_L0%yqmkCXqeyRNlg#Jb;P!-S!{H)AOv{Qpvp zty@>#x~8)E0hvH?L9@eFHtTB?uK()m@AHhXC`fKVcjk{cFD|z0pjcBt`ii?)zt(QS z0G8>`Dy~5utxnN&p7Y4+Pb>Xr*xxV44pUUaBhL^9*rf`Z!QW+tIb)?jJ`xp>ma2`g7 zS5j#L3U0%w-kWoMl@I^Gw&2>qwt%+1S$kexB$eAT+BM_9%W6fvj~c`YXRS4p-~;kKD63_yV%EhnIWj@Ek~Q zCMo`hOY^78#O*QD?QxU95iL8WS>Nt4(?Gx2!zNdpM9Swp(xh-YqgwqVXFFsL9mfk! zof}#2svG|8M zdSe2APW~A~icJOWqdqw%9Hgr|bDzBNkv=-%;}CTIJ$veY4lCy z<9K2I1g}hJ?hX~> zWKlbgl4qME*6uO5oWa$hE}vdZ5R5EO>Hl<<=R6zDw9f z=}~`%?NR3(b8oaw!O1M5#3>_tNd0{TayRc1toLJe zg7V({?&kjGKI)aYUNrN-(I@yzd9gZi@h0V$L9yTu^hce~i{{2Yq3;5@#kC8;%#PFC z$K`u}p!VmtS@u`NR|G+$Cyv6KIQb$D6*2QQy8vAwYoAeEs{GjYSWEMU->Tg*!STZ} zC&S`Le3z^&hi7hNo7+QwzQ}tJ+hBH`|J0f1v2~$qz=U>*|AzV{u#7g@cLnX6U(_Dr7svF6gC1419p}xq@fgM3@r^+jwH+Kv(RokSB7~Q ztil%d7-fdKio6PYX-%jZ4>ku@#|g7f<5vUqx>YCx#2;7>?SYMdBKQAGQ4_$r+h6C8o;{Koh-hF$=h{6k_- zZOr7XCw5bu^iE?|e3NujbrW{edXv{UoO#2Qb?Et?`YHUc5!U|v-3bsHn1M!gNi5Sj zaq^USn|Po2l=(*eN)4n&53C7{=+796yPdzAe_4CGfaQ9{AGCh%c-8&9_{9Iz{rL0Q z9o3TQ#3;=z)CBd8{GRxx2%J}{djLgc1}_L=LLvJC{f-i%SYTo%VV>w`VWvR2BPQWN z!@0=xWMhun@Rx+|RiVSRalqur>nRqL_J2Q|x;Joypwns1B ziSWz?#aLz)hsv5DeeWEY%_KOQe(;oD0pWL{GDNeO_C@0zd`R=w*OL~KHk5XdhPJzy z#zIq`whT%0SFK_vZ7QuMjfQ4f)uLjiBI!W|tAhTyt(;z7TY)rp9gLL)uGm>MKn&EU_X_3Xs1v!s@%{+EYfinS0iF% z`dtGu0iqJhvWx(66=(r<@){HR5bFL_JP0%r{mHUUUWubgLkNO z^MurZ2p#Ml1Roq8Gz~T$OdVt^^A^74d-DcyucS~f%c&;%-cvKYHM9A_A!6W6;Y8X= z*=BeiWW?;QbdC~ed;atHep)GD5Fq!Sfwui`qy8w>Y>63eC`-v5fA)K^`tYNx`cRdw zQ=wde>=%!@^H0D}Qgcv%1&z$JNc=N0wic6RBd6u1EHZS65t(R!$lWy@|&4|?`n#eHw_tP@T6GZ^OT5s%SGO2 zBPo;-)WcyyFowa2j#5DXMWcO8kB|{D6)CUQ6%9WEuQTpf>IxLAVpS7qtL9V_e@t6= zOtUY}F5c+NF7_@C55tDu6lS7yv_9TM)gdCtQaLR|w}JaNV&rPrYWSu|uIL{TUgZUsGuTH_;DZA7W6-Gt=rV- zETMtr@%QN!vv>8fxrG`h^H1s(EAJja=t#YPJ$)nku$S<_$v*+sv(^k-@MUck^@@K^ z3$xlO?R_^Hzt>xPgRQM~{V1)WmylHHAb6a@{_M1xT2n92mE|MD2imS9aD?-J0fMF0 z#~A{-#hg9E$p@pwUSxSuh)@VniH`md6Q#7%G1K$WmxtZJ$$$j*_{LZ(GWR1i-$^Ke-scdFDxo7eB)98uXU(z^?548-r(ip~D zEf4KO4q7P0{+Yim*~@5!Jp%mQr~J)@2>~6E`IZrx8K(IPH$*pFTTPjVW02cS)kE4d zxhB}d(GyyihHa3`v!;pX@KO(V15aGGvmj17{lgG?NHWBAXnYWmzNtVuAHh^+d&c;D z2aZ?qlE+;1ld<;bW{*zgyHjx+SVCUjvI&rbKyWx+J^&{ywoAGJE#- ztNJXxZ|RY?=I~*u@^!UbQL#|E09T<%6`?su0K_u#cjm}6n8!`vq_c3A&TuiAij0lKP2#GuS(yE{n3>EfRL)c`Y^N(7G|X7cTFhHaST!zQvy7G%(u?O-u7Rmov@Y;8>AqEpNP12lR?^~KL;P4A%jJEh zAUlApnY)?&_tA7LJ!NA7k#>QFSPofj&@}G*1(_|`g~&xG*GXQ4p7o#*g%+0e!~6@T z1gEQgj9sjfbOqNNKDjU1`|wJ#XmTki^8V+bX|i9Wx^`RKCw-bL(bZv9@@a zq**)}A7?GSt%!z#&v#w4cB*J3nQwE2#Vm#Ptg%MEBXyg?=wL}5@bRDy z>YBV6ulmxZIj2R(>aj0UC%T-%yKX*rHEZQuH>a&&6W4K7f+*P1esWWxw*ZTlL;*bU zq{Sn62OcQ2hq1h6YC(uZK)N~WheyMKX74ne;ZASg7ig!!wp>jlUYT$NkXn@{x`L)w z5tv)xwzkPYEfBZ9lDirQIuTCtC|<2ERYm2@AQwd6l@i_h7m3PQc8N~=FE6Kf_n~N~ z|H>HZ5GU^|$A$jbvW!3K35#SQyc&1YVVl*K0tmyD4YF0X#4Be>@aN(gjTMouDDpGKHN|pD=!AWR z8Wrjj8lKY2uwNr3xRQ2kTaXsEY^>Z=-E7_L-6UK2`3bg-7T>d|-fT;uQNN_lH&2~+ z3r?$zWFPAsogcj(LmXw=Gp?E!4Jul|_(#EOT zRrt~9Wp@=nb?m*&Jid*zJq$L53ScYbC+3h5V6R^C=NP353-Sx~vsMpuQ+c?0NxHcY zg{F8aR1=xy{o>G@W5H{yM`FEaQuXut3Vk{bE>nzLJe;3#?i&tH!!K1zroknv1g>Wr zW*VjnQuz6OEsUg|FE71=S2o~Vu$NCu7<{Yy8GXGyQv1`LIiIM`jv}|Pk~3{un4@l5yKH|#fZTRO~dW_D701w8xczE&zD^SYO z+(dR38^F?LE-r5~4*+<;)NpY*cR6>(bHQ`Xan<48&eJI44PBnqcAPEsUgZn5oCV-( zrxKJ;#xcsfwAboYwI`oU0J^!Bk{#N%Ubk@RRZy(03oKg%EHJNs^QG5;a_(r;A@ z^23%ioS9tghW&i-4RFiuqyef3Y9QoAeA@!N3GQGq9M2qiTEq1W0MFW;Q*2h*00C;> zViq9qLA-kJ8G;6)cj9HuTcHpJg7qdHeWV|H6kKhAXUpgsNkVZmi4?{c1Zy>-_6PwmCobcHo zv)RXioftbQgt+36hVIcDZK4WgLGFFP7_SB`j}e$+4M_&y2xVnDznE8;BQ4)6?-6W$ z;)^6eT0(QP4QwQ6na{BF(jpwy-YSRHS=R9m-u=i=hZsWwwRa57ONZ%-wzlLL`o<#F zJh)C23fyfLr7FMYKYhS%@9GO_;)L;R859Kn(!2FfS|IlH-}}b;Fgfx}ZbmSn6#8bq ze(rxYB#3xBQ0(LtUcCLKDZEA^7q|e8Qi1hYJ27pPambyBK~3t8eHt2P4sBYB>;$UP zlUhD$4U?L{d?6f&_~>kPAxWD{_&p2^YQZ)Bsxu=?FBIA&E*fUJMHaqC3j3hDu4(2z zp^uQn99)iBhvqK3cGDQPv{@HLId+6WH_z#*g)tw`!$k6;F=bdJ829e!EM%A=&@P`s zY6S%@7)h`qy<`v&A1r$LSKO(c*KY~(cit?m??;_1tD+`Qyv*Z?u0?H7@uWOVO zmeZ+Q1a$aMqVBbZX>__8gbiSLzeQK*we09+ohpVjF4U@N5=FEN!yvCi%-OYQ5O}88 zH&X8y|DMQAi|^6zF|#87Cu^Q0jy?Pm)5(t7EEtJ#QomVzspvN@HY#b&C_<#XHUg?H zSv09o<^q-RhIjeQyu?#V51!potv76vtHvM{9SB+d`&OU4P6l1T6$-0X^9oj1g{YY? zM%Ej|BtED>q3qtetPklvHK|lh{rcw>eo_XE0=P(DF&{*Vq4JxEH-pd?*|;H0N0FO> z%2V169-hqjSsewCI!Qa?ej5qi56PJg!|<$hlj~LxVGh#jHQ@A{!sRpI2q;+tk&Pv3 zypNsSsF9`#{jWt8X{|c1U$SD?nHocJP5KNev8n&kpf-dfeUM-#*>z~rS#T@=-l-KV z{=W4@wE*lR-4tE|_+{bG`RUtiWRe-^;&1&+!Qb#s0UaXWiwo4EZN5LMUFaxm5Y3)X zHf{XingZ(WLNNNlVX2KLQ78zM-Eq&ZRRXNqvI&kgR>(jD(aIm>Z@Gqju3!Kd&mRa& z{9tp^D>6TJjdVURJPo0~Lu{6fvNcv3YS1EY{*s!J*}a0c|7<6A5eRB$p8@g~3iTO8 zsv`@{iK}Cyyy%{q7YNk}lFSq;U08kH^vM&rGKnCCd^nb<2P!{J*BH2AH1h{1 z4)_#u+AebLOnpW8oM>PjGT$Sv=#u)eh3L_@TaB76Li3O66#4LHlq8qtje9PYwu0HK(%{C-Ez;_CB3*(x}r8at`R`p_arrIXlCF$ zc}aSf-y5}o01NB{cUjQjKCb#bSQxwXrdh^;jrC4=Ze4(fPx5coh7a{9#>9)(5*?5h z(@tw*9~ZgRti^)$5dh;u&C^(S4SthO(JQmT>RC)|uhn>}yludCdV!3Ky$*IzRlf-C zZV~?GU0OlCPzWPN1SAg4_ajaErXw@&s!x>f5zUwH;l=Hs^kx3U35X>cr#DW3FGX@C zaMi}q3|lnz z2}6Vy99~Jb#+Kf;FKD`F{jN@I`II|zC)}aFP=-@oCt}+U<=J1P;#{?w{wFDVp*?T7 zn>aWEc^C|5BL^_?&dvc0(YR%?7McUs;GOKf!msHKp{fw|wnNpiQ}~NP@fdzw7)-^+ zIA=7wh^Anq;}8K|K`!~eI1~uiRi(yJ6>5QUZ>>HhXWMP`@EttkXQdL;okeNQBlE#q z;~3uhXK|AtLhu9rwefaFyl*6H>=$3GHzpb@^9?uhHy}alM)GxKz_BW)T%@Gj9^#wm zb)S3d6XQ}NtW}~$Mf7?BJJ(>uUR0YH%9V^X3)73R9>m><4rh6I4HDUgV;r zW@+Z6sm=$|=V;-Ahx)LOr|8%A5A)yTY@p?d0b2ynz|=~oF^_r-^F7k3*IQV=uz%4O$t?<#Qg5*In-p=m89PR zbKwMFa|lQN{mnQ%r+~j#ertQxV2fddYUC@qX1p_nrY^A&0Lvk z=;U?kKpZMjd9S-__ec>gecT4ql{t(fFoVP#xv~fsUFeMSCR?Z36QPCH#C~j*6>Dwa z)CY<%?zGsN3`+P3*pynN3)Gf*c7f@#}v|dG-_0DH>z7BTN@5{87Eq^wRr=5 zhNIc~B+=ria3q-x!tM}5?@C1E&{jzk5sFs?$o#oM%*82tuE zJk)3lD7sRy1ursGGR574v{8cugxk15AwR(o??Ww4Sfb-K!pK8(Uj-cASB8*>m%tvuNzSq41J|$u^&t3q|c>$&f)Y^9sWBg;-qgscuW0@oRWBz0QCBHj& zvwmE2QsJhK@JqBQGmPP@##bN2ji#nfI@fm;S9P}@*-sD{_Ak5ZN+B$5MLAD_P zbf*_x+JZNY?z_nBAwFH&lrc=|G$aC&D)Nt5J?h5mu4smIgEDbtyAr{_J@)$VrYaW) zTkA~f5~04K_l^Kxspp5^tXD|`Ls}sQ&5-6g)@q!Iaho-Y!1eT``sq^g)}wD#`N!pk6+ZN7wAB?Ez^*KNzchYtlpGj zjipJ5W0Bqj37yJgWu%b+*aT=V$O8uv+LL&_u=&5vkgu+|xrai5;cplbrs<(8|ExST`__Ui=p46_Nz2&^swTR&-eCEFY40IKI zb|f15nmz0ZR8>S*Amw|2t&-z=a~jjl1VoOk~9I< zw67e~>CnrDK!*O|%O&$xOE11t4~|m-ebgD$yK~L2K6bp(dUEM9F=ep(#&uuN2dqsEPbR zo$P_GcdX$F6{D11S|^3G_5oaX!S?-<>Y9=#@5oitP6hs7Y74>BVegiPAIN{cJLkvj ztT&K*v~cU>^z0SM1MGX`zD&P1*ZIGl^Vb8p9qMMZ8{4oNPdKnFL=P%P>Z5tl{-D25 z63OwoXm>xlh`$+(esaNk%AY;9J^-)(x|h=(jeHSz-1~Rcnr9ZYEAW+HK3c44b&X%y z^>5xAzkE6f4D_@_WB25&jk;apIi}VF&z^k1@0g9%nR=BSzqNd?W*?4y;d{baaz~#^ zzIRL>dA6dH}8BLM(-5v6eNA8UFZvsYCS7FIo+~P^l@I+*3)m8 zo*ADQUpPn4gFMqc<2~~`X1h1K5q~ask9Y6-!t|8xpnnkWD^Mee_-?esO-AgvQq)ol zYJ3A63H7Z&|Jp=--&Q$sT)Dm5(%t%P%N&}?3q`^(?rIz-} zif>t-nWiP5_m_AjJ15}ikqRNz|O;e{*s3_#+a`My-nyI7(ot))f2(_00sO_#9LH735zNvwUB7@=0VtR0tv%Z8=L zQxvH3rFjbJFa52XvoEEeDQBBr_$P~xU>ZJCpx56Y9fSgs_hsil5u@7q~LZi+kd*Z8LY z)i4RI?mgbX;??43&|cs6-u!8^lIsru-gp<#FPZ!M$_L<2zJ28_?W1An zE#1HQz_YZvv(c!LJ@V%Qd}^l&nC?gM=5=3v)G~<;w9&%`{sQ`d2KVjvE%tpLn%-vB zW$A_VdHX*cx@Rr_T$$JEjZKWLfAxn4Vm&il>n?f9?Jm8p=eU>fjJwY>HadFmPVJ0s zvvm2_0u|0QpnY2vn>?y+9{bo#4@n!|J0-7-cQ>S=t^O5|jl+l860*OaewdsXm)sm~ zlzlX1QuKFA85})@NbeOdC;qfBS)cos*3H}B7h7vXt_nv zZ@nMpqUo%wDNk}E^`cvbBDxDaR+&QHpxw#`j7OVFU0-x6jiB)-N1H-Yp>2O37CiV~ z1wnhk9%W(Ju#GuG4UmB#M<2Mx7C{=6DIh@Mg^tj%FI4%EFZfeQ;d#(c=LN-3qR2dx z#OMM9VCe#J*FqBf6};5vXVFSuBG^J9bUwTf{%Ld)0`6HwJP0Xk!TTr zQjk9uT0w;%qz`Xh{3kiErRc6sAY?zt2ZDOz{?(MF+-*N>zOu(X=Y(4@GO@=Nqz~rfNQ;2KfEDto zcha{b3>((jb8wzxupr?_@dEDiqA_tKZ8tUwkzW^zA0PP&mJso&QT<2;?AK zq@`M9MJMIo3q&`R^M`o+5k>=IfS@;}wF}|X zOc{Ci5SL$Fgbm(L%-SE4K6FEtj^xy5d%L#5gL^@=&`36ly&_||wpk{Sd1Hf&*WqfA zMgE!K3wg5$|G;J@)d}u}b}J=LehpUmM*TP(E*nFkQTK!{jmwk{s#S? zi>$g4D2Acbl~BaWfLeO%e>NZ6bik-gdrftdozO@=H}>nzA-WP7gErEL-p{rqi@ri7 z3${dK2K7NaziRwEu*C|s-lTW^M?1<6qepb_K@hi`7t=lukg$}McgDY^y~mZhK<+br zO((llGCL!?^PRk}Ha$21*NyHLEUdBpmEa43J2WUZ*#E;Wb_ug{dRN_6^7ZTAzo@1J zaKRX6!gUs3qE?{9RqVi2P9AvsF>)27xZ`u1-em?+O=Y&|ESYN~Uo;aZ6|}<+kGdC6LS!pW!+2n!56EZEgu!A{Oq# z4JJS=cdP0k^R+AfxtzyA(T0g;!kpY{nx*(_MO6S->X&PVYXczteoN>VtCJ0aX+Hc9 z`pNjpAvDtg9+V{W_SE0{^c!7K6bc%9dIx$E?Se*gP7O{CZVgnj4doY5Csbv~gn7Ej zyuO0t)8vkg`T`cZS8CuKqDnD#1_ANhIzYdlds6+mVZrQ zjY!5*W~+o{THfOddSsgYT5}5SNVh?>q4bip0sSbqG-^J;@Pg-xClbdF|6W@Y`tukt zPKl$(dv@-TPEE1QA(dm#6^q6?GZsd$mKLU$;_!(EMTMY%lcSWz9P(N^5psw&MwLb> zCzU1DKMAWUeZLciOX2GwQm;xeBb`;Kl&Pe+H4OWkN{OI`3wCzSqj}9nlW5eMrScc8 zbgjf%PngCpK@Ma7$*t2zN;~QGf}eE8jzhs=!^b0GrpIAnscJrqUK+_=EcI#8RR9L{uMoOwx0km#=2ot?f6wK|fuFa#miscE{UkBIrzFxl!2n51 z^hFKzjrPS|6I}9zJU5P`VsdG8YxHPzcii*2*g1Oc>iFf^`Dgq6fN1CRu?zWEVpWbb zz_$1iS0{b9D^ayiF`1Dn9#?dvmXf%lebs-}f6%`@admuU{DR<^2l1UFMNr;-wNrtb zUG<`8l>P$U2~ujUw}zo&v#D3HQ?XSs@EE1PO|hW6S23Uv>;U_h*@cnb`?IE z#a*Y;bpXhrtGYRcCbdGY?J%Pfa)uFrwvLs0BJDRjBXz2Cx27}ul0OP(&PthT^+w-T3UXuN z%pzsL*}nXeso|b6(leGh$l;)S2CZb#Mbk<-R84sK1G>xiIZ(|$ro!yKlqpS%;BM}Uut=LIZ4waihhAm6c z8jKI@&n~QV4m>D@Fj}FrtvYvxC%r(cKU1+08Zr7x;wSbgBZAtN^k!1R$5pD~NQsE` z&*~`Rkbvc5pp7&G@B0=x)Ele5!@3 zh_Z1C!SI{4DeK|i6cQR>{hGqZi89ZWJDz)mLY(eguiEE-tlNQYL^Dsz4eKuuZG^Hwm*)vz+>li#v0UTDD|h7f<$=F`gZvlP;hg`+a%*>se&FB7R*3Mj z-o4gwEyP@jsI!JDVSQ8`3u?PSOt$22J)|p|%=ZL~{7=P{Gt9-Q5y)S!&d(JY2*55k zWG}_QH}da}d!jKdW9eUaS9tfzGvVk11OUX4{ERDgAuo`Ex0_#eQ~&=RR~!w8{J7GdiDVSQe(Xj2mQm|Ls|^ zM3Iv$Xcg7%+V8nwZ65%g+U=vt2@RckHuY0vLJ>odh#VofdDiHAuRc5N-%~AnbZ_ph z&JVHhb*(h%y`Om69ZP)Xx(3ciq24td`8EIKG*ToqMr4y7RU+JpY*~){0~fYtYk3f#5;&4nVmTM~Y7Wj&)zc7T?1E<0}^g(S-y>3qJJR6G9m~OG4=mh?)^>W=d<({9Xu2ycWQbwh>W*S#d zhI48Q9@HKCw7LC=D}C%Pdsg+^p$e*o;)7d-fADO-SBr#&k3%KAB(pKQOz;>hd|AgG z*voDL;qc#fWe#pSd0w?+)^-~*i3E_!qu#rm%5}ZN-|+uiwRY%sWw>&=oIe+CzQL|| z1H?mZn9>=_xO;mJqo$yzV5p<3qsf!~Qe;jx=IrAS3f0;sc@Qk#rv+)P4Go@X{?Qma zuU4vGzRz9O&M>-I^}_RLxZA8cKcDo|1O4n$B>?uU4#RdC)D-)iIWf&!J!JYhEMx1v zxOM1l8OYbB7`Ig*Zcrg=qE%hH#A=v1nmMh+FMGD1I24w?{^izuG~G3&>u!lPX6xh3 zB4((Va{SjYetU6uCwS4k=k@5(zT8KAu2w~KbMZJZET;eH7{4lZNrtk4ss1XAi}4O| zgj0KI$~@hJ%jY-x*CV#h6V?prZ?C~F+LacIo8Xi-pc5tz(Ih(~0aBOFwCXD^vbaJC z3T_?B>wU&DpMtPrI`Zy)1iys*V16P;wZy+VO}K?ol?U@hF!2NT!LPfJ93k3*>hna- z#tiv{G2>!a5@yt=br*ANK~4%Zp#9eak-p2==o$lS2tK91bq{xIWqjno4_5{L3GIP= zH7`wv@TT%JA?g8o>kA`*(uw5$yP5@_8^VX=C!EPx!i(C>GhJ_J??}2C#03ca{hMlt zU>u>e2eCU!FeBJzh4+n&^M4O=0HhwoU z|EDKfI{{Img<3_Stx%h04B7X;6BLb*g4=YITk&Vq7JXl+4Xgq=>G%&rL_2AgB@rm7QwDParr8o$vEG#t)T$}4${PUhH(d6D_jmAm7(!=1W4aAaX0=HRmT$)WOCMemFP zJefqgp9z^SGQ9&!!MRx6{1839*G8+Z1hgE6WeWJ58C;JjlH%X=vA z8EWZJgyykG&qB&0@-Hn)p#mcQ=9Bb+1&Lrk(c_vLgrj|H*>V2+wRAAx+Ko`0;lz?Vk%EI#<3TJ(=b=N)HLnp#yvPFeKYh%fU1Atjbaft_l9Os-2I{fX(y z{ILy6%k99!UO6o-5H+~ zn*eW{o&nQcE55Spl9b8vlx%M#OHq0g2_y+EiFBVDA9*wT1iDE-JN%FQM9dK-^5e)QqA<8=CL4odx*O+KVuG)7|%Y|HyWX@FPJBKh`3Ro4G{3 zmU=nXQr5FWMX~rs819c3+87l1`Sy7@VWT7@)Qw zS&{OQr5Y1wQKcHCxVB!=dcdNbV|w3VW0fe0P+`?5iJBf`&!CbVcgbKBq-L`y!6k1n zPkZW|NLw+j>Xg_3bdzpUZ&DA9>g`wWFh2V|`>D|4%-HVOw}l)aSvZyJ(_Z{ebPL91 zN}sjeiFRt!XB@Nc{P(?^LTjee6e2H=^rHCMy`e0D&N|I8PoGjNQkK{=`gI?e=`5dO zvxk2T7C$yf(UxXOiZbTvlv9=@lrDs0l=eyUsV1K`J$g8xwRd~tsls5Cz!VX_7kwka zBJm;mA^HW6R{c>8Szv3Gx;-FjtPwjMSM~lrPf60K4A<`Z1=<~sCm?UERT(oH zokcQbPP+69u>K3307oWAN{wi9;8#WRBbH0llcf6E-z(q~ta8qen_-F2eK;jnf9xwem{3!e=An}5yN%EYhy+vn@5wPme zC={i4e|Ud5_;YMJX9pvtG`OI3I!G`+S|!NJ+K%1v6yk%)8|>DVfZ5?&?DvyKuj)Wm zn5)6=HdRcyPCX1TRWq8VhH;l^Yafx?{SQ2P_&f2HAf5gSS*`6zSYM}JJ0pWe4$ zq>T^^1AgOteW?oL-)}J4DY7P9#6kI$MtKL?1%8pa%L@UIh*nLXoS2?AEK^P ziHAONAEMUEab8^-#x+*SH3eCG&yPi>$ac#d`?-mxKY0rOm~gMhnM=(iZjU(b@xt)J z@P+Y4#mElB4VP*2@C@;e5n>QxnE4EU9lQ^Jld3k0GwUv#Qt{X6(DoX%S#jKg4a_Oy zm|xr=*is6<#k^AljA|M^8vvf z#0hLY6_s+hX05a4NjWiTE6O zq<Wl}^qtRGtGgA}WCLu7Skx2d$;9{vz;eY+xKDEjr5C8R z{VBHY5A$xb1+g_9^eIvF0a^56Caug@^Fe^Sbi|MJUbiVPTK@*m{dc9TPxEFizs^jV z_PvgLc6Iv!;>vf+?6P1$k?iF1S8xYJNBjb^SYQj1IIZf}Vhr3|rQ0$_F&s7`Ga5EH z^GNy2M@Qhbum?uajq!M-V*|ycR!Z&zSNaRn(t;+g8@gWK-ou>083F-4#uK>7$Fjxddz>Sh_4UFZn_PL! zr<0diu?HxVSO@VFRJX(MxllEfN%>H20yBgfT6xt*_g#(ENbfxIoiN^ZqoE3N7?-ma+2QRdKtO~IGzvP=5wf`C?b z!EM>b@sITEK&*7K+epTQP#5=P`jU<+$Z*Owmlk2$1WHyyo5w2M7~d@RH1jv4th_?Q zXn$BqL>g>}KhC_fR1q4z!1QAkJTa~vE;b^ zko!`OlKc1Rd1Ue@8qZp?%6^clt!TOPeqsWp?_pkwuSgHs4+LjC=UnT~tKP?-o1neT zyVXx4rgDMT&r`~D2IdXmiV$Y{@sb}dNtfl^k!#IS&G|fXw8$zN{-~e2!~EJn4(>Fz z3ID5Oj8(W9zn-XS(%s<%_2l4z`()_^rDbs2#GBUb=0xhjyd|PbtsXGZq8z0wmat0= ze^}P!OQwx0c#5BRv=mKdX6{MWtg|w;T#`iNdUiM*^!jTH$PpQ$}Fs{|V8?;!wa;!M1bmH`QzLX4JqxDbbJ6C0&vV?W&G#+f!jB!5twRrc8 zHm^K)l*$puF5Mn$DKL-fO87oDZolYx8Jt||9m|Y^-aBxFRPHzwu^qfmoYy4;n7KSmq2{zg>U8vb+;l^H_^lLDCLN# zCK%}4{x`Edlp^}tVTf>k8K~OIOSFJ5ko)@)7wJ>@3Zp}`qI!OSu|TbfB%x7)({qzN zQiwF;)08oM-BQ|bC~0!lXmGwOq$q@f1;H$OI!ZnJXRw-mm5mZ}b)|`cE5xMRCh_zv z`Nd%@o@6ykxN*0N)dB&MWZ|s*jA9Bewi#a^qgjn(FSjirn<9EP%M@S89~Tco z!Jq`kUNpx$=Wjp10dRM44{$GVtD*IE6Af{QC6?{& z<9iMwd{dd=Y_^hEkOVvlH=5`(+N)JhPFwT@pa1!_QzkZL~=2b!w5mC2V}#* z+cL9Y&!8=KYi!PG9?2}CxLL(N$8J=7FFe6h-+~C|oL8|v?;E4f^Vqm7aSnwKw=O;> z_z#t>(E8vSW_Y4p9phZnc&KX`ry{E|oa;STc@}vfAA#OI?!Np3Ji|PjI)~W~Q_f3# z1lh4umR5)&mCjwxX6|`M#5edp1z9-yb?Y_ibIZlHIeOV^IUU*FIk4H`M1OLIv#E2e z;HEl4@LV$(<9CC`cW>H6;}En47N@tJ@0}H?;;Y@xej@!uaw2p=5{YZ~47B-W0~Sgs z+-~pKF4K_%+K%%DCB`+W(}oij8WhR|vy9z<96{GmSY=sqgy*2I_#|yovZI6tIg>3> zl-?XER9wI*5!%2xvBwz%4U&M8SLL@Yv*ih zZs+6?1rdSVKCIkYIeCzlk*(^FnlXgW!|NWRwkWJtABME8+_v8+k6|>a zne3or4aAoGvS5s}u`JJ%RZ3k=g#uh}eyws9yA75JfN{UeeEmSNssCAeDHl8YA4PuY zs!q`Zb(w(@D?H(uxw4gK`Mcr`d2wFm#zq;}xXce+C*MU}vGaHp#{VMuF0}dl zz-we?mojAEpFWZA(Se}mBMl+xVlQG>wdxJ2*1c^v?IlD#8QGW)k5GR-fwN`SmET?X~I`( z`i0phYG3l-@1LL^?MEp9m)VA9MY=3QofF#w?|7HKz2d)w_3U<$Hl9#VeXuSWdZFOK zdoNicI$uA@vG)pWe_7rqh1{g>_g}WYQ_DXq4PLJ}mc^|Zsd;QKohCpye<|+CkVZ~c zQGS53;MTlNUMcbnBl4inOYF z{Jg7Gt4P}Y<`i`(%QxV^*X<#pIoQ$T(m{SCK`Z`?P47!M)rN70}H_gzQOW1!XnAEsd(4!(NJ1 zbIfH9yJkg1MXU(e`V?9bU&^Zk%Tcdv(cXf%bU=GsT3zoMPuhF*@i=SFnH!f_+t8DHepD%FYhEAgIql>f0DHL>lpy}R=$+WT zY%@Ri+;2I@{xC?lBYp=I5%`DNQ)^0|e8k<3pQFd?(?#O{IEQ^3!CDRgx%zl>obQ3L zNMox01tu99S0@`7B3gVB6eD?a=j}A79D#fz)8`MWFQMAd+z2t34LZu%xoU4&m9E;N zktl?kKW`3dz0wcb?JkcdO&*z$iGrZ>0jWwE_k$*`Foo`-;>3HgM1=|SWbcq076~ycW!FyspD-l zvVae{?rcQ1A=@4BxprHIv2UC#aExur65jlK675w{6gfxSaQ|2k+Htp7N8;V*y)pDg zB{AS7TCy7(6?*BIv&nZ4dZ(&%Kj$*&vgtWKF?dzbz633eb#@O;JEog5m3O}i$69$aqx`q3I@N;f!>?#`S2P1;<`B@LbM>=)hP z0k5~AT!E?Q_&r&C;Gn^&hr{Rloqs;L{!O~KSuUPOGM=5=O;hKFtM7aE&KeMT|9mHn zs<&D=Hz8sY$@hQoD8{8@IiYU@c@&mLbeur?q&R3@;*803SN_HN3Y|?(!Rt_dP3yGo zsHFxM$*}uqp8B~-R{PLxBJ=F&2a6E8+R?!GCVmDZx*3Ef(`s>o!qg$K8(eK-VLB;p zg1eF|&2kNEweJRV(Z}{)b&4#$rtNnHus464(Bk{$mn}t0t(_`4^|DGC7O>Txif^i# z^=;-ft!)}zbqP`jIPI*s45ZU)H+Y&iI5i#6cMUWYx&*2=JO!YTH@;~)@K$gIEb8?v zy>@%#Y43Evp2Qj=Io#=9UhY&|5M9#iI_&Q-s3Ki9RJhymT?^JtU|hElaQK{ybSYGB zj4fU%mWFXj-G4&Hx|=WJe5>LTzlJF)+29#^Ej*djx<@YddQIzz0Td;E^WwRvc+|h9 zYIKF^c~K1Vy4CIqj&_%<=K8(Z(f`yPcwbWGrA2W0RKdl!$d_|MWI?otO@~&O_S5oY-h)#y2md57>HCg2v@^y~hj+_V-U)w+ zCD(aLyO7Rvr4WkTSuN}JEq}=;WQ$3e%COmYdujh>bA0=8NejU`(7y25+@~9uzpVSn zMcSGEa_}GAs-=OJ_HycyYt#9MZo7T$$1o_%R;zp32w$6Sbx^RKxac(Ti35A8UXHrk zaW)d;h3}duMzM2c({a@@ozmc5@lV!!MYCIf+!EjW34{NW`JwqhC`rZR2;VhKZN)$H zebkkFs6zDS-Q+!<^@39LYM@*s(pVUdrbk(S)LDXMJ&|MM zPs_NhQDX}-@cT2YR3g}u3Zz68_95HC0(~df}?A- zVS!5$3oFRlyMXQmN3^DHNQOTWog8jxn&&@6h}18o!X(b#N8*QG_vw5uMR%aK1E6V_ zQoDcl*-hw0-WMou#?OhfxWVXO)8VIg4SVK!=p=Mq@n18U(?s}EM^9Jfh`P%jeY)&S zLro0DpcgOIl~(FksrGtb;W(m;4VQqDRQ=WhIeQ?9*8(4Icl`8@Em*G|4s>qs zYfJHvhHUz`z+|qg0PEvxaPHG4xjF9kJzzA0fwV_O_ZX9{ZG`y{~=Mn%IV& zJR5~!0TY-}QKpfp_}OZq8)1ayr&c?UWaX+YQ2(-nJtd9?KAsuF!g#(8(Fu;biP{ly zZ4RrLdHf4_s-teiHnP+$VfQ}n(I`mYA+6uj^1iT~>KCLQ^qmOl@^oXw#UuU!s;Z^J zUC2RS*5vO*FB`!<=V7c&w69()_)o8(2c}Ffn+L_dTR0}+0>(bJs#`Y?`pM0a4A+cI zce^gQ*w!X<*$%{~o-^=FS0eKG(d3&L{u-6RCH_n$L~FF_JWgQ(agPX2c>+-Ef~3uE zSy9wM&80A!@t0%Yg@D$pM>>c6Gv`d_%F#Tj-SG;DS@{Z=k#kYh=@0hk~ z4!XJjD^dG2#zwVUEiq31X>DuNRAAF;a7OM%si) zIaw!fWLv^{Sl~Kh?n!$p$&3_3=8G4t6G~Nm^NVC=GlGdrcXLL~h;@Jqx0>f^dDbb&;WPL5S1&)@xvOfS)A7I) zb?T!M%Ts(OibXti64-Z@0a3q28MDk6FN(${|E{Y!CZvkwX_xHZ#oMnT=r)KzO%R#; zDrE18%lGW0;&KOgQ%R=2bnierBTe;Nj&jAib^^jdEvs6SLE%4Is2r{@nY@Uy)ql3` zXplDH*#lhfIPfi_ftIC(n2jVsVHzA0k$WNo{RB_E-7ICBFjZ63ub840sTlFDrU*k- z+E%SgkAWdS!0+Cc3_H&Cyt5)MUL=XT8>#=Wjv398%lG_kdFcH!{ZQTI(>|}LJqonq zNkC5*Ij+KnjZ_oZTdTTN8_`t-ud+Ba)`xaj#!rCop6daQ}*89YR!LOwA`#o!^N}&2(U9a#x z&1>=B>iFu1;K{L8imCW@likOGo8W9`fwHi=Bmbo~5^7%azRF@L$=}BIL0N$a4pcuc zBEIO0)wiGnL7y~o@Vw){@1CNMrSK;@5H-uwgJT^KhAmbfx@}l*L;`u=UHo%Nmv3ad z*P{+02?b%&`1!52IQ)_Vf4+70wLg4q%=Q8*q+P!*N|tr`ca>iTT#hAB{QSV$2;=)n z8ILi&(@eBvC*&h$VCVBd}BQwMx- z{vbK333dzopgYQtri9G?G>>~?c(RzN%L`j+gY$6(?W@deimyY>OeS2ZNdJZHZk#GH z=Y_s=3OQbq+ih$(U6ga^ZxYyWawHhBTX#fo3|wQbTRo{c4i|hUU1dvuSy>f6iNw>Z zj}t>K8nDdLML~N}|OD$Mqqd9$@*4}K+oibLp&f%`q>{(22 zTpX=A3DH`U0i6tJ-m8!E@?hOMp2ldq(pG!n7k78Y&Py(q7o4msyRw$KLX@3%D&6bL z#1>R1GAQn-AKL3$aKcR+n3i4|bi3|7y@NTdiURWjHF0~&(F*Zn^^{;Q2LHLHAvYV& z>-!maP5OIGWO+~UEEq4C0kH^bwpc{gbF3Gg^c24w;{iBJ)k~?PHv@-cOXH}Y z+j;AB1t5pdLOU~dE803T$3JkGEm?UFgkZWeYs5(2@qRLc7V4T`7*n$4RWp^n{%``-&gNnOhUbK3(Jrn#PNlsySHQ5&Q)vAZeT*2P!KuPlp zUWk@2ybls(U8tJRzlV+z%Kw<%T?W=XsF%GHX#3AB(kx&#JM|L|^a9&Q=duOu!@g;PgHjZCy7f z*)+;BrvIeT{TLUrYHyy~%e9~85YZ|h9`l7=ifyfObIHlxm;NYBUZ7i`DR`@59 zi$>7kVkFf6J*5AFJgz#nIHlnGyR4k7mV(5l_5CK0D}fi!=sdaqySx-4&0@wGZn}1* zL|GrOCi%V?{2mGNUIF6FPt`_sDVM4W6_5MS;GAJ@wq4gOaV>Bbr$R%F$PA~c*`hbx zr3pLYP*+k&Pa!r|ez9SiF4g-n@wm9h!?)JWp*%+o17xG?xx|5gdCdHv%Gb6KaRO6P zENKf&{Rv7<9~FqpM%dK_Qlh z*3M09lR(7`Gp2hHS*)o=+4o&ct@|SfWUZ|i;oI<0ATH| zyo=k;icOF-rmuu@h=lk$z#fYPl?1aTIzA$PNQ^R3KG@uAG_4$9V6GxCU7RTpabg#U zi;XheQD*=M&)bV5c*PU65FK0!p?R5EWD*4g>jphWK1Qz%gb#!d67875{qdH_R@WR| zI4M#_$)%|;pTreLzZpaY1|tmSJRS-;^;ALU!gj*7ZhyRzWYT5@XhD)d33ArGjH8Ur zW0d;blX)Vy2G?1Fbx3HKaerG!@i4wmsSzl`0>gp8flOETzyAQ*eM_8gRNZpg zlHIDPar#Bm%NAe-KAknzirh-v3f}67e_X;LFn|y4@D!$&fE$ln`pHcIk{gyA$79Q7PK4we|F>3LkA>wDpJzW` z!Yhw01Y^Is{!19{5Kk=3SCU7VGo@qo)r>t29rc5iJ)D+=&Cd9-d(E5NN3~S&Qq&0(v+zbk7G2c0lf&N9W~PSlGt#_~>bpkzI1v5U6k@jYzO z2>4V&m~9UG;Af#%9=!?{w#!SRgZo$Cj~u0G=cUK9t_QCrucg~6tIMY4q$YhNfs2~% z_u1!ft-wZ@bSNl)KX3BB^D}aXcDrRpHF|6IsAX_QpJdP;RV-0+%DL zl71OXz*A+>nCCVCZ2G-^_N`;uBe!JBZ9h0$wGymDULZO2>Rq|{u=UCO#PJcgQK4&( z9J31k0K zv+++S4zMQn35dKQ{w{6DtG#W5<)`t*_4Y&Vs^h%Z`vDLv4TcBpM~%Kyz?~$F-e*;u z#EvUrk#Nf#)-K_~eBeo=gkTzj%QmDtdYF`(MYGJ}U2&*kdD_-i-Cug-seu?OMfcX7 ztM`kBUvsDqR_^aZg;ZVJibm7by_DAI>pn!u2 zCTMSYr#82xQRT@cly3FHGXH1CT+;<-sS86JrP(Sj|GyqBrWbwe6Hhv`P*^(s#3=)s zu8sn;YOHcyCaqRLwNz_=>Z*0&f)jxRf>JSxC#~w?jjOASxmgu__VlzRIQR+cZ8YSsj7Y~ zypHeqbNfEatt6&lTi46`F_MYQTlTFB80Vw$Xd0(3KekP zAp!%U+@yqnw0?N9Xzgf@LEZsF0p)Wk!o?4u|4!952fyR&xA`XwErkFou*07Oecu)Q zvUtq~{wxHSHPY-NtCXP3!(uvb8Q9G4Yq4GkntQvO#HOnLB(Yf^D~|jW-*6 z%ID8@Hn`P<&d+9jfvHHFu{LKSZW0gFT{CWBPhXN%G-Vwt*nM zvFd}QHoJAhY#EQ-jnD$5@r^6kT5)N;b5^=6RMg7A4!#Wc_Lu-5U#45nz`~vy&{3AO9 z{6pW6Kk+@uKIsSzdV2yFh`3)EZhSt7ZXiCm_S~U@I+ei`f-w=xMFo|%5)KjhZy-d3 z{iIM6aS&*^+m)5go^PRI4Fv;F_Aw~Be1ZZE1%DF~=1}9F6l0E^7GuQR&B0^+r`$Wi zr`|ci4|%nPjCr*|VRY#SmD8sim!!*;6{O2CFOO5IYwXo4@70U9RbjSO&^YPPIcaF^ z)vN4P1UjwIIxVX2RTPhC=p|Oh0kjNKDsur^zP_jMZc#oDQ9j5~K1h2@7>!XWaZxFX zQ7%YW$JtnI`j~C{SZ(+;P8^zhz#u0MjlCDy#L5hSma_y1J4&BHg^^1Fq#31u5v5;^ z)y9w6wwkY>8>MfD)wW9G)TFg{l0UMDwtu6#cTzO6sAJv580E7ODFZAs zOECINfVRJD1pu`20a^)wWjG0tu>=S;#ij_L_0{3K1ZYehYmcCEgiA5e zq0>+hFr0L=rs5jGkG+ixyOdoGZrFz*Iq2Iuo-|hOh9con>(Oz2vE#?dz-Zsl6e(=GE@nbvLSpujZ-!HoB%` z>)mxfG57%BW&P~AqC6K+GGBOyE1;+}Lt+ghAfq%pfA>v5j_sJoE7vez=MK;%&NOf9 z6>3<#`~$g5(P1X<2V<9v!|WP+{J}hgS2*@uhq~0|?CYJ?OMqZe{0}r=`MoC3^F0HH zm5eiaT*P`*0*z9eBbfH&i{IYIg6(<4`LM@i?NOi-n`7Qg@#Xng_6++1nmgF`4Ex{Z zE_DASTjq&eaQ-RGADMWG`|$;pPyhXMwF3`H9O^lkmcAKhyg(s)Q@qs3+w_~GjGL7xj0 z(r??e((^US4v(hgdvA;KGwRzKrW}#ik6!PhgAK4Qb3$9&Q3}mfT zc>JOPnL?eHMU-&^k?bKV6^JpDUyE!xytQn15Q~j7>#uL%)wnRbciX64Ly4Xb$p zFWB`sLq|fQdtT@-$JS0^*`d-0gadUr(%5Zq0*}~tD-cfU>*PYohkKNs5`L5pcxl^s zXghjHu?51M)HL90ljlvTd%%(y$OHXpSOW^b9=UD zA7_Q%1)Kg_;h$ebboU)7uSiFa^lsQE?y#ni?esSFRShJKJ{&)~s@4d`eza7rQL^hD zBbDFioy0)SEBl#+SBr}`v+Ct3 z9c<|i{qF)fa}&d*1LqE!>kP?Bs7y_A`94!neD>boaDEu|IhGe`K-aFfqY-*-j`*af zkkCZ9a|QZML2E~>z3-@pPdraRhG~v~U)d;P>?K(kU0hPw78~N@drAzTPzD!ItbbHH zLTNZSJX4w~anu|k17Y@r9UWc(h%G}=DNVZ~#+bZDk+%+R0)GcduLS$4-fN;|!qgJJAx5XqXS&LCiTU)`{e+|? z#w}z+=*pO_l^bhEM%RR21V1v{@dQhR5bG~B)L?Vbg+%DY5v&7z=bX{dC-QijExj2a zE}Fe~hauXgQ$w%A%ON1nX+ew@4Y3n;EXntZ><->H)-jXk_Zo%M2(}O+io|W=^M(o?XP76kL=_Se&WKP~N;CTtU5)l~%PACb&S@o; zE-0P=TvOX+a`NdPiV#MO!*7rHF%@p0&yTfatsYXopZk8VS$}w89cSp$rg>JZ)pgRd z6t2X&vY&BYOVn5hNGsegC*vr9@gh(dPi>yPsiCy{qP#IKq}1G@pv99rxZ`?J1SD8e z#C?O7CznYAPk|ew#0z%q27B=2UZK}WoS<-C$^3I2V>`Z0ANfIr*Nb-4^2#&&Y#jen>fPwUr9Kbw2~zILkq3{g6&A-XUgXqQR2=){iU2BeXdR*0m>g|htE)D$-rA2i`+2nC-DA7 z+^vt)o$KV^WfDn_AxJn4qEnG|)@Lz=@IiqQI{eHSd%mtb9oI)$j%i{56|&Cx7B!k& z{UaIUJ91tEmOFJDT`spiMdgW72Li`TKETC$a1?r`^D`oQIx-*pl~4VBHT(0jDc8al zqb%MQx-Zn41J?ESIr_rTJ@*|OrZ-^fsg$0EIiB-6UW|@I^gnOf|6x4+J8tu z*$sI-ReTKcU)wxB+?@O;ze0WLybm(E6a;s@QfnF7r2F@ZHg$*}>>G9SEcU!tKZ=j@ zmeKfpnW_qA(9)ZFZm#!zZ6MLeoX6AF=JOM)iKgbyJ>@dhK#zkX2~RA)+<{mO-Dbw_ zwhyuCdh83<)fIdOPuv=}uS;~<$2anOhG(Sj_jW~|tnc&Ad%u=(rxMM6JktwI&)+6d z2F`Wt04<`JlFXB2-4fp^a?BItog()V^okPrNtH(BZsNA_#&B7vV)(5J zkav5z*%a`&hLW#MeSR5EfLVT@%Eld*Hz~--kf(1*&>5p(m61nyB?7@?AYzUosxz|D zN!lbd zd<0LI2YE9oEPLObCSVGO%*rtkjz{cEYh>I(3oJG~&2(@OHEaCRLwITj-H~Rd7_i60 zh>;Fyzz70s7xmlK@xy>TdNc*hh&y7E~k<1=lEubVov3>_7pStS90KL<>4;Uj%turHE#`4Zb(z@~Zs>A-` zhybK}SyU=~Cb?tBvZ^NdF(y`BGM>^EUWq#(ye7%aGE;wDY&mGox+2f0_B6}G%%H8* z%(hmz5Rhhy(8n_AhEepZH6sp);M;3{NKYfKE=InJQ!0^m4iG#LHdYO$)^+d zTQEOA)}`$Uaa2s*#x3n}+!OfvESy&K8MQ_PXgSi0u`hc&ZWuh2y4f~^NqS#z$)6p4 zph*4<-(0`R3Px^DT<)OUP(R5FW=-!SK1;nhf8>xNgat(!Q!bzrLTUXD-J)^9_JV_i z@;51BR*XP34bdhuOgIl#8TFFVGl^*KN45(^Y!2aS7T0Pd5d2I0uU5ckHY`Sj06or* z4SLOzwpjtHaZ;Z(_Gc%ydBULt`~E)NPfr*TrUdz1NakNCTlGF zpV)k`uWpsOxB?EZTAoC+8Ge#jz47nlZR_WxE1U6eLuo3HJDWp6{x0C}fCkQp=B5L* z#ROKtYp8$huaE{c%&6y%g5RHzP4}U$ik!bdJfeAxvXO-N^&5LGK`gk{@^1>`Lc0n= zKT(jWMt}<%{9+gYkH^ABkPD=SqYG{7)j}i)ne0^yu@l9=GU)dsBYHB~4*3c5M6=zQ zfb^D>Kpqf^=@bgFE%-C%O4G%b{vn3AM8qjaj&BiD`B?x5{@I9fhvCc1*6JuFQ65uKBOzuaka3 zzEyltT7gYtxbBc|p$wGn$Zbsza?z&!q%A<#*kVyvw8I(rSXdD8EsC27A=JSH6ebkm8WuNAW46mF7TrlGbPiqF=#Rz%9CL z%~oVoXm2^I-)g}3?rMx{R0dL4B&pN%CTo;e81%ZAJyak=3YGDe@Iv#8rIxot@yoet zq$0kB(JB=~8kTwGdm+5lowS`GT`dC(u;EJ&7S-*b)|Kt5IAQHenpM{zT2z)*_`q7! z_{s?&-8Hz>Iw0NEyeql_+?Bm6y}=$e9#v2v1ce3EiJRx!RtWK8kA>MMrmX-Uh7iLal1GN@@Syu(<{jgfn z!vVrS`phiB=R&&kj}0#ghI#2JX&BOid^ehxV-xgob20!Tnoh_FKpdM4 zJzkn0a2-J}Y0d(OMw1by08fMxPa~~|N~DaS7c<8LjHAkkQ=lb^i))hABP41>R7sk1 z04mXRf=BShmq_ce6PqHcB+Q8br>HvN(&zx02nw=#;>7!iDk*bF00HVsz=)(cC!iiJ z(Of(UO-9lj1CWKf5+aQNpou6Vy@3P>{l{dXt;CF2iZ_$iLnkss6akW=q-gjKrLXGjVeqKnLnd=!m_z8+koJBD?q;s*IR9 z3cwq6C3wV8`~gspljs|9CT30octu-@AMq1MCUpW!gp4R6Pl}V~1xTUsM2)D3$B{XG zOEilhl-lD445RS`j=+m6lHQO5gaBU#FT$KODR4waToK@eo~Ra)ExLyZutMXB9?=nJ zMH>~Tz({nC$QIip1RSFA#E*E2Gm<)iCn83elO=^pV*_x|+9F0I#2Lw);1lH{^dTi92k0YsJ*ER77{iO`qY;{`0EwFQnKiQALj zU;u>3zD!%h9v~@tL_yph;DnK=7qKq7hYe^!Yl|JR6Td+n6{A2&^bx;78oON0iArikJRgI7)1B6DbOYAWMSQkfGq7xX^9*9+mF8(A>{RROR`?yskk;wpRM3P*` ztQMDKi;+M6NHlIsy8>2!qSXvE_TOV( z|371%)A@l%+JKf3JJJ&26a8f-M9h%^&ZvDV$svVQMZEA*3g~f}2`3b1j_42EMZAE^ z95qExv9%nJB;+EtYcc_864rokR3G9!PLHOd?ECEd5h0E_KIxKiP5b}F*jon0)iu$= z!5tC^?(Xhx3GM`UcXyrO!5xCTyE_vcf)DQQ8W`LzdF6R;)pvht5LyGhpwHoG-bVSohAaApuSs&xoi?yw-*%EaHm_bJ z4-Y)KBYFT2(L7$US*E@=2_bJmhol%&^i;Wm`k}8Z5G%g8-o4PaXJ&^`7`0icd3UlR zv8mtnnIHspAiW!)!G;Kyqp5ipG9rNF??QACFDj6a`Osit1k0V&yaQ_>cTSq_vA(d?bKh}d$kaE7jVNQV)HmUc%*yyV zt{O0j{1SB?wkx!DUsSQYCiHT)^(^3>NOO*@_(qFGx(;hThS3*mZ#HBmEZi%)r#&U@ zrK7pg34a0!H#{WBG=E$c2i}!7)d!uXs&Y}QL=OAFrPJ&gPp=QB$Tn-C)4u4|?WRFM z8>?-9xScpvoP6Q;F-+~$|9E~@6f!ww{PNnVfgI}nDK~UC7I?LKx#q@+p;0yvc9E0z$I9-Ki?# zLcbGTQx%KhC}nTFY7*2=+1Fb_YuGYQuBEQqYOeLO{ik9e&t+|)OzQJ;l=CM6MU{X_ zrcDf$ht{6o`hubZ^T(W~)Op|6oY$rMBX6RR)tlU-@zcS;;b1OlN?W;5q|egq_!D^!md@ z=86@mjnch?Bx3bH^c{S`�}e`>iOzolh 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≷j#B=z|YD_<$S>JP~** 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

lG84#5ATxnH1>`9pPXU<)WEPNFK%NHj zG?1r(%my+W$ZR0b0C@(;GeAm#lmaOQ@+^>NfjkRj4v;xO<^XvP$a6rR12PxLTp)9S zJP+h~AkPDt2V@?Qc|cwO@&b?-fXoLnAIN+lF9LZH$csQ009gQJ0g#t~yaePWAPa#k z1hNpw%RpWR@-mP`Ko$X61mqPUuK;-k$WkCnfh+~`I*`|aybfd;kYzxY0eJ(+8$jLw zvK+{AAj^Tg3FJ*6Zvt5XWCf5FK;8oK7Ld1qtOT+W$VwpZ0C@+{ti>w$a-h_}Ban?iHUjww$VWgv01;`d4p91+5$frQI0@(^=E0BK!`8SY% z1K9>-8<1^4J_GU@5J|V2C=X4PhoIYK|HT<2srVdpJGB!fayJl3x1)D6MpE%P=yuuD zamGk0J_p?{dp(5o0MY}<4M1)Has!YYAUQyCfZPb=Mj$r=$pw-NBp1j{KyCtZ6Of)j zdIISQ_ zA3yX5bh|vUo%(TpiGXeoqNEL?qy^m`L`fS&Nej9?h>|vlk`{D(5G8F8B`xUoAWGUG zN?OqE!Noup16d5@RUoedc@@YKAWMKO0kRj!ULbpc>;tk7$UY$Zf$Rq&>2}#icE(65 zJ_p?{PX{Ap7m!^*z5?_-_3{wMe!7DT^Jj?&1!w%of;?rbLaT$J;0 zTQRxIJjX5{a@K>~>8U;A7%%U5{G%uIeL+%SWPyBa!qOmRWL;Rrin4KR0xMyY*;I@L zm9n|m4NWkcdbcyEr#q8cAU^81m$HzULv03EJWp+aF!kdXvQ(!2ddmNag3KVIgK##u zTy4f-Mv{@U)Z{#fG4y0CMwg|Ts7Jx&_~T@HG6k#ADpsT!7@MMY0-43AAxlszmY_U- z9Q9!d()@AMge6Gr$598CAe|pa?N@>tuLQMR32L?y)Mh29!Aek;KaRSq1oc%3>ZlUb zOC_j_N>Kllpp1VUHBSj@n-bJ8C8$;YUwh{P*F@4b@XhYhAwh~D0!A!&)D2R^N=NBU z6f6*Wksd?^g+$?y!wM)x5fM==s7O&!Q4jueV-O#^pG-D2Xev%6&KNZNZDzLSB6OAj^!d2rNS9s^ZmU8nxW1G8 zuG_y7$FGIw11aFCh~Xc%kL7!GLnLNUap5UMd^|YgmZKe}6sW&)pOuo~JBXti&V5## zTDbUcm#44!Zu7A&IgX0|b|Y%U(oqRPj*~tLU!>&76eo_l{+IVRaPD8sNg*4~NV@$G zD+eQ`8r@JDK0Y&sbHk4FHrMFHX0*c@j?Yp&AGtWQtn4{s>!TB+(GRKNKhq0fgp|0? z1(%6<+rsCgw)&B8m;5ojKa(r;AC@bW^R|f`1cO<_?D-Fiz@1>e9}MRE!NeSJVk~rI z%jyt&>*|8$*0%9dF3yu_jJv<)x5PS@8!I#2i za5LVvWyh1niKp`@#PiQ$NJv)4iKincp7Z}kJim_uw=4X*{7m42qoxS3bESTX(f>1OGZxtHjEke?8Ul~LKeHLShyFm}F5#d|aSU zt6{9+tTFY^#pch_ny?Hm{lpTw{PYsC)3EfXPs7rmJ`Kx$`ZOFVVGI-nRxbbNBqYIG z08_pz?Y}GG=AT}|t^a|9FnA_62~a2j0z`kw$lYirLlkl)xr$s(zCylAt|8Zw>&Vy0 z_2dR}Bl$YHiQEi*YejC{B{UQ4vQ4Z`_O%r&zp-jKxs6;2SOr)Ocm?n(U=3g`U>)E! zz;o4YW}r5Ig&6z3VmIsZ6MDkLZbM!I~=#f z>Y+zL9`=kl`u%g4xEn6YL`onfh!RW*Vb?)&3%QltP9dHTpH>6=a)lOR9R@K_tMkBb z;2V2>iU6^H8+#2j_E?he7--66QU6I_D^z2ueWy;a}+@%eYY@fie~t0n7o0E6cQtT9R#&Y(l>1-X}ucoK7Rc_kXJ;p@EkRLDxq(_bG;+4yD!S zTKVDJ&(H~{3vK7Of9>*w#Bc6 z&GaoqoLJeIsvzhGF>_p77^q=@I`PA(;vnRW;OiHtdZ=@E^jK@Lgm@qcJZBJ|7+idV zer|>40bnjB^+@-C@i&-zv=_fZ5+SJOHAKz~bsHT5pV+z9`sfAXNPkQC{7# zDa#F^dj#UqfoU(TpB88fs4cWKR{Xkz9~X!#7@02>q&<#4)Q)bibg#FchTjWl11K^G z0mK9-H-sD3HO!7euXeAI-w3E2$RTJB!~`@A2n_@c7&oLF&NYHQ=RRZvy%C6FbsB~V@PN@!~kYlu#y6^Ipt6|fb!6{r;?H}Gq+9ok-`UfJGt zKUa_|Fc(l4@CT3wu&wW50k3;VkO<#Ltyl`W0bq2X+HkGb!aOcuK&T?{c98nB7Ti3p zJm@^~Jj^`yokwSW5Pc$|8ECIQKVm-wej?6oNq!!q z_R;|>zVl&>TrKGDIa%9ALMuH-dmoPNeLnA!%5RH()JZ>v`!2gBm6-tpdYF%D;m;#h zx0^ZvtFHX#tYQ-9_GG9dhUYtE9{QExbQ@l9jJHO*u<>p?2us3RuSulR-)1S&P}p;w zCcF~30{ThlVBq%-)egw=Nm4MR1(~%O0VOPZQNR-GBo?y{T&Yn=IDfF~&;YK%-j5?x z!l;MfPzqM-;#dvkYQ2Wiu!5XT&Gfsj1OK5f&0@KD-s43Dig!N{Qb6d4pT8OrPh!JJ z2N=wrpag05P$6x#>~0dHz%OK6r=Wo+fY2QQGC+n$qG^QNM0SIlSrn`h9D1pL_Jk4a z{kpgsgSjq+H8S2KiKU2*?#R^h>%o=5NFYpbxh^IjD$ssh27oW*4I=fJ`=(sRcWvN`FNI&RwvyrStRHh3h zjZWl!DtbT7p~E)4&BnqODQ=#;%Ycz6`D1(K3@;Z$EgLb4XhM6e1bgwzK;Br@&dJk} z+%{PK;w6=Rx}wF#)s3_D95nJ*iJtqF{-BT(9$0+1!N3iyZE$+&O+KXa+2(#g%={`qB)|+1icfh>WvK}0Ckl`;8DTwAoki6 z4h^PqHRzoWry?+tUvicUTCWi^Z9j;FnWNZ=W~J`ZlE+2kexL?)Dv+Kdb0J z$4}g3Vfh@hD%koh=cE_P&8EJP{(@P>r`3_z41LnyAR)RiihsTSdNVA1)0mVV0B88R z(dqW(Xm3vdMLL8-LK^sWe2>%aW=X)zw068mmp;7ubDn+EmcHuXug_Uh6CC3x^QCVA zV`$jlh$P>Vh*SJ8&(C4Id)}>)NP^bQgTLZG%AlisH%g{R0I$MM7 z?vp5^;^LAny-ZPdsv~J`;9D!q@WnGqRJB~en96N1fHnrwjh_P@{P?w~$IND$&q-LA*Ijj4m9$e%at6-P)Qd$(Rf2TLSFy~Y04hP&%R%g9CY!y2~#+rqYcRnng z@mtZa*-^lK&3}%tsLm|K-AnD5(-19Ze-*^`jgns=d;4}mWXt}z(e>)X89QY_bIGrL zMr&%t?bH4_&}C+6G&Ldyn~N$#7;#`F;~-UT*#->kI`g{o{^8oCn0ceDJs+XQPv36% zG!}qdicwM)Qf6eGUE0UHK!o(+oc(IHb_KdRY1w4fS6uz*6{q2`7q9l9i;Spw_7hX< z@LUgj*UYU%!PChjW6dB7HSHa&r0Qt~BFYxs9*ajXyFksvx*>^YATC?`it`}3wPm7YL@*^X9J)lc{W9A7 zL4=hUK8cM4M;2Y|%}hZ(4_R&tT1{O3tlC^|dr5FNgN}esA3|7iJIfhWHeRk^Ld4t| z{tI^Y<}d}#7&1d=D~`Gmhp&DpQx7-;aXau4zDTb8gUNTM%?(P5@fkzo9o5Ie`mS6_ zHQMp&$%WA1!mEn6D6f%OqNuc^sG20dUW(@k>UKd!647H8GDp+(Mk=SaZ*g`x=dKI@gO5* zsmnF_{3MEYR=I$lEP^IoG`ht}%T8Zip@-u3i%54OvkuA;{Z=x5o1;Y6#i`9~M<$N# z1nlgSw@bB7PITRLuuy>nV&+I=sT-YIq;CbfjxRHd$8rsNOk{zSlr5}HxSWzt(nq6g zcb$IQqyCb55>UV1Ogy{I!~i<=ZHl#qU?uG_)Imd3tr3oU`&Br$M%UbAndC*qQHgb0 zEjR3uk7OQy)0TVFL`rqB`9xNr9x#%yS#fZACX3YTJmw*dnY^5$eqUi)C6cbR)r)7e-oMVn4sq=CRkN9T z(UNm&akkJz2IY{pXq*yv?waFTmhDF6Y<>E%wh<~S><{d;59FskdI z`rd=v0O3I7MNegQ&*}uB0go|5EShDgQrsVuj2-b?h}47mc#!C*=uWDbGM~@=?FaJZ zy6Bep9^ZB3hH19CMNZqop9BVe)#Yf(^*p2e7W}1L0%FS0-$o>ZrZkNbiRwQgAOb&J zfoE{x#vj!+r=Tbtjbxy9LVtRPDMyqf5|EI~D`NNFtZJ@xVcYh5`~>PTF@7$E(o%BY zrKvb4j8`*vvv!u~Sj8|zQDEBbOR8>Az6NRsg_m4_s_W6uL7uB+=56wIHD_IE?c8!p z3*)bf4L0+Fn35|#jxk2nL!zQ(U%s10vN}z99nPF|maKV#-9yWyyyp1Os=by-No#n$ zG_bIG#=}i607tvU*!$EbFIV0AU^E;R7qo7hlkR!OP3`FACC(i+J-9BvXj=n|JgRnV z-3}dE#!9S`D?ObWF4Zf-7UbMdP_}A?>uMLGs1uCKY|jb>FitP7VsqAx&fgxwy^|_q zhZm#YqT|VtDwmLdciYL36LyKsCdH>{>q3slu8yO&Mb-%ec**Dk;>>B{HD9&w&0NaF z8GXuO$cTL#r8G#aZ2fo7zYfbNVMsvBt*<@>W4pN@x^?yW6B2t5L3tC;>fEtmlmJB| z+GN^m1HNJEZa!8lKK!057HTM`Mb7BkxVxB48nu*AVBIJf_-MRli-szx63mQgv6`Vs zTgdI}Oc|=|OIs{dv?|e0TyY7yD?MxXx$JY}vJT=A$c&IN-6k_S64Bcw5Nv8_D#lsU zoM2g4WdGcPDm$;dv|NIX;29cqH#N0Bt+SdU@CaYD)(%~B=i)rAAK!GtcI>qA+xErS zJ#pgaB%N-@7Y2G{pk|u)Dp536GQ`dx!k6Sf<%7Aao2|(*;4bVTbr-hZ##cf=RQ}wS zBCc7sH#o1xkC(nY(0WZAly+WTa$B`AR#@F9CPw5t2fqBNADq5bG--aakFgrVyi$3@n_7T^?ce-|2HW3_?AR&B}ClH6PS(J^uNB-S< zM|1L8d8qat88htd)^enKBBZ($SXmWfByG3tJa*34bEECn;yV?P&!6w$l%<+Th~G4t zs+P10*xgHXOYd#x>XO|brX;3Uo<2&pQa;a2Dt*;|XrJiC{QQ|*v|OXJtYANSkRN~GpaQl=tj@bHhhmTv( z?9=ZP-I!S)_zzp^uhV?okGu`R7JTLC&xk!ejSW3>`F$^}`|GF|yzk{2e2@A`<8`%S z1O`lX?TolCIbC?;xG2glB}e?lviZv{Cz$g37iVz$s<8~4;+_>-(951|WL zq6Zw1yeIoIW>q<3^b@}NuRq94%+0mZBK7+#{C;b6R5!Dh(6O(O`?&gXlUZI@?O30j zt?cYp6-dtqd(pj_n zUd&90M)lYbz z)5dvAz!Vae(}vKb215<4Ga%X7V-L!w>#-G%yG@8 z8jx=^<`No;^mwE0nv@?X>n;h=>u9hn3ptE2J7~Elu?e_#Dkn(O<{lry01oZJnDe+@ zV>b0m=~oPWETOI>y0l=$GK6YHlr&xtf~$G(7)+Ovq+L|C&aLkZ>l~uipfsR1oifqp zFNHr_%!kO-`qagFk+)J6@viW2s>`At=5`8t=2ayU_0N6#j7lxhs&wQjUC5aXN2>)h z-;eh0CY&qJC_7q@JVcS)wE11ID$hDhpSOC*o1O>zFs;5mSxm3dwzi-T|F+!FBlUh2 zkeodCEQc~fy4wbFrHZtR|B_NyxxG9OrV2C}AG!SIpz$*u*{WQZTjbJb`R={k^4WL( zg?XL}JZ(#uirC5JYMqO5=b-Ss0=)6oS=-f#7lH0KZfG_Q z?cE>wrV@SP>(`7k5rNiFB8L3Vtn*#8rM)dLju+#ntnz-8 zrVJXkE}Lk^A2GB@Jz2C~v0Jbkg{I#=e0CWZk%M_MOEEh*inr8@Zg<>mB1V>;hzV0i zwSJL-t(R;q`fyK?j5@nM3HQ|NyfB#|i&k&$HaM#^>kVY<*coq4n#-KmtW5WOCp*(_ zSEeg(c~=%t&e%Y(>`NkO8L4@eT51DG=T|Ds>NYYlAEUV9ne4AH-+>!d7d0nDD;55% zS;T8yYH@jM;A#kX0;Dn~{Bk?F7^yM_pW^8Ocit+zF(#ex`eoaB`hFI8i`;Vxqbv4^ z2s3V=%h~HH-0|cvl&q@iV;?R3QD&ZCy|8|Ri+b z0m;IySUKJHlLG*v#uoEM20Ko6Gmya$Gwj@?PmKYi5~=cP+4JLNB78#P^>dFzYunc` z7@FWPAt%KY^=lb4XAR2l8v6jmO-kn%6;UWBp5dsUnve8+HKGf0baXI>0Un1M2L_G_ zNd_;mCB@Y|-XZ%VXv`es3{Y&hiFM%#=8|b(!_zl@GY73d{qeyxI}m^~7zMEW|ZP2p{_ z`;bW;6v?8)G>96*dlPPR!jc}gjz*)a=buqY;aLe!iO{Yi;`%d`JtHeBVqRbTG&`b6 zFL4lDZ`q3TP{4>-uyuD7I>M>HCo_AyQA^B)v`(BXZDF*VB!3$4^?gR?ATujze}_U> zb$C(-sLD&E?fBlP;!+H&qL^CL(4i$`YBbUJg`l^bh41LA(?3^r7ZSK?ecVW4*=Uk~{xb&?Y2 zZoDfl4cNZ0zB?4nVe%`Ykyej;CrpllaUB{tHu52oA@A^@v&&@#) zk?xjFde#2O^lH6iKg%t`bm)^t+ig{Qwpv={71ze)5lgfOb#>idwM#oH70334&P#F5 zu&7XCn6tw(g6(q^Dx9Jb6jl0OjwS{+h@WS6 zhL(sdj6@7Xe^0o%=|wH9fhLahqSgjL6JZl0J7W`iX%kyBpg9o>BO@>GfBq3pJX~98 z+%}K_Dz)g(d}P)OXq1&A@n``g&Vwr}ZdJ($h^0}k24=VH%I32w z8)(Tb9knaBJRvLGk)z8!%MuF`$`mVLKWAmpdn&22RQXh(sRZ z3>6I+=_-ym2oJabsaQA&k65@oKXx1xm_VK$Qh*x-m;j9*KZ+C)${-pF3>ehy&_ZVv z6fxN>PheV9J~5fVDAB);tMhW;n(C0geaYouV_Jj-Fm>m|92Otg2!W2T5+^4nHCkFo zniZidLu-*0A*dCtq+nhlAU~=W_+@-_-2h?Cm}iQ+4x&!~($@I??@qIhg7HkAoQ*n3-6Z z|Kn~(A{GWl7A6*YWp{fMdZmBy<$vyX%R2Xl)mL6z{Ajbw{xvy~={-r4J`rR*ibUei zPj(&#Q=d*0@BKx-cc*&@J?e+KR{2ubQo$l1XsEVvCPLGutXWgbRav{Ipt*P{dg5b) ziZ#wE>#{ryuqntka#OG1bnuQ zTOWP8YqK{fKRY}|+w&mFiWkZqvk?DV4pHeeme#kmV06F^; zfGQ}tYz~8iD~xUhP=*6dkTYf@@+1|2s1Ea&TBbueLiU4&@E1CK7KnAd;2WRB&=?Cm z*YyBjFlhk#xUcULCvyq{me4hVxQW}KK0v+q>Y zs&9`Q*@M_+!Q1o*gjOLu$T8ci_|8cvk$&J z_qDA7$?Tat1u{N zBM7iYTDvP(gP2@$V&r{(&T{NM>gEF85Le^L}+f(^nkD(x$9u?#VFYf^!Yw~ zHFB+7sg`4bcK2Imrn?`a+d;IO#!#|`DDeDpTSagPAD^uhnM~=*O zss96d$J_T=%ObYdQ>-gjOrID)-wtCwlP3@eg@_}I6UTZod5!OX{Baqj^7l#%DNGUj z*-Tm9m<%Eg%YX0t|D1>+_t)rO_RXDm)_y)IBrsqD_Fs4JrsX>w^$g_-K7zIL<-I^` zjvwoW8o}g4mi%V>fjQhKZoq*s=mr@A4VO9~osTkq;#x)*5`NdA)QJfNeDP5seeqdD zSFc78ZMag?flC}L)%Sn;Hi6pX7kXFPD|Ea)>5B>p3bA(_I zzQh%c3vIgltu-blI2w9RdK4H!Ak^~5%nAA5HXsSPmqk;UCXs=RtdW5MuTEEx?Et!BL3I=nGn!E`XU9QK`pi#`}P? z)qZOVL@Wb87ta>&fx_g7YwrtR2)v`d4!|AYy*6Ay<}Cshz3q)yw1E(x zQy>L2{qJ(+VT;8p@(?TtDdet#yQ3Zg;ffaJgBziqi#|Y`q8>uK#i1EAq2J<=f-H*y z^Pntv7D_&lB#vVnL7|B75uC+CiQaG#g8juq`-qAL@_FB|qx?wb;%EGTUlBw-;0g;3 zVCIwzDCQU$xZfzVhL6>;{DF8;!XAJqNnfyi>}-%yqHLJLc~5=RC;>qFEb|UaQ8WYF zo0@rmgLo;?Cb(Ju304jqS{&PeaFH1!0FkWt1G;3uxQKZNp@=!&2caPk_QX0Z0Em%0`|PpAKo5BkTuDi`fm1q6@$lpY~5r^P!s%Y)_jdW#D=Pr3vN~pAK{v z*TT|)x}Coo_}sKH2d@u02Or-JjoBwyggL+|PVes&ulw`Zq!#PsS1rT|dM!&AZnbeE zx@O$x3R|2m;!){#u+%J!eU! z@H5*6gc?y7cD1kvyml}UeNM-oT08oVtrhCzacAvL>)Kn?7i5`O2XQ?99Yt0D@75S?1}>M%W#ldib5K=#_tF{1$u*Q5Ui*aTnnU?ls6p#NCKJ?i}>4r?@ZJ zwpm2K~P!TfoGt5TJotZuKoY=0IIHA$qkv-Wh?ZFW>JkG z*G41|e_D4rY8bi&s+AH@rPA?^!)Hb>vrP)l&)R111y zXYhS)yAMs=RS&msH||cvX<~0^pU68MQP+X1 zxNc~!_-^DLv1icExOX0}(POb|=pw=a!g;P;-yM-6UlSm5XV4v*8|NF>)7ST?J7&?( z;X4A)u5OSgd^;*dp4e~XojLEcUPyLCZNJ0Bav0--d>%_g`iD~TlV$Q|3Iv2Yph7S?GvWMiH;h^1=rc6Xd1XY5e}U#=p5 z=IxsMW9j_6im~ANj-Ef@WnpnBRPT2nmt&dCqR*WG$+C&gNM#So%1L0k$?LAzMw#d( z3+P^Cpf%<`^pYqDVn~B7kNbG}149z=BE)j*_D4lcN8-!y#V)~eSGSC_b!!suJ#-6{ zrw!G?;f*y76rn3_D3Z*}Y4oKRA`4c&2M#+h!8#vzMVMH*;2~}g8{pXk|H4w@{vWI? zlh~?L&%3-;hZxM!Z%rfeh!Pm-iowLn1rr_$rKt)A1rdofrcHFpwQdS2clEMUD~6<<2cVtdVcj7pim9S>2K$a14y$m>kFX2DBeC+Tet%Sh zD<-R9AEy4tRsAiViTNiUv8aKSbr!CvS#Y!8{{N~KGxo2<4ePlSE?H>50NpR6nsx_t zTrtuY98i%03%F_cVxy}LK)|WnGX@frM%0=^jCT0kIR_3)7D6pVG&Wbv6htRQfG!H8 zz%r!vcMqCkcC2WquG!vksO;H1tgli2TLsHzteesrUj;fRGR@TovAp$4%~%ZZ$P?v5 zF_dV;xZO5$+{MHPTp>d1TCn*CX2Hy01OAIlVETvY$gDjuKe2tGtU7Miyb*^quwdWB zpGp=WvEzKF(KeNb$%H)4STuj)3};HUm-d`n*ClKsU5Mj9tM!sbT+a;)R1lmMouzD<)(h z#B_v{Y-fStKayvJkg4M>{5MdqVIPlvMk>9AIqwXPGQv=eq_6-J7@%9kseHzSbq5F( zh`X;35WtwvY$r$|1pR!}XH5mK{6l7m5zlW2s+-VDBw5h99rN9^xg`rfH9X~XL^s8LG!qVek98(|qBL-Gl05_crzVO@yj;O9l7@Qak4lCY&5dFoOlA^inh4fJwZ{7(y|q!<6g@Q2p)H3wq` zO@UrggCZ9^Bh0}lCM%}&NwOis1O&3ep&-I2|1n#|Bx!1>!ann)D5$TZ0TBX)#wZ`L z-;R9)X+FrgVirWYlMAa}5^+BrNcorQ4I}@Ok$-haoV%q{!HPqp9wMi%eiEWGUx8)( zL=qK3`lM;9hANGy0DZw;5NCm35-*LKaJGV-YSKiZQy}Cx;U~@q74*rNLVX|v(~8h5q*YC)TUACU#V00_NrrP znaBSto;bOGu~}lw29CiN$U>VwUtLP;^s*WbD`G5Ks?fSr6#XFUSgc;FD#xa=jkFwV z>@SM3W{XVt#yLUOpiw$%TDbV*ZUPta)Az$Mg4P9DjNmhdZg<(ni_3E`(&9umS$V}^>#k;eRsV+9$?@NaNI_*Q?z z)6xNWOf~tdUr~d!Pz6&?=?NCp4dU3k)eUCwFT#vR$&rOdZy$+i8DU_1^gK_NL^}m)^B}h4-;I1INjg>@)hnc zYG1y8?EK>s^?%Gv167XCXr?cT9&(N(Hfm7INt+n6)Z=9&L6tM%_>RLbSl|$g%p)VOC?&DOl!qS*-V`#qluDsmHSb> z&)!{+lL`3Wgakv*b9ayNeFcmNJ6eAk_ufMb^A6i&=>*u3jZ~vm1F-RFSw3;*y4XSB z%8rqyR;{pX!l`8YDq6p7+N2p>zGdo=Ny~FP`Sox1{OF4@;{Lzb0s3j@AbZq*H)SFF z$mykQL4;8X>Gb>N6mA4gv^^>NqE6SxpW}=H@ zyN#R|Y>T$HYu2YfaW&%QIu51HqpVS{rUf*JcpqG4Ik zf4923Pl1uMXus5ySh=Qdz)Dwl?G+KnPFR>fP3hxH^o7CmcNQRX%c6+WWAy~smhFQ4 zHW#>f^}xYR7rJ>d>I8^+J$oB@C^bs@hqps$0}C{Upsol&Q)V~xhJw}(KdZsWVlaPw zhMhbNlvBj_CZ1ww{dgH_Y69NZZ8=#}1xjh67V#Ifk$0FlGK> zT30OXW!=R%h_;{K6L5LJ{JZYlVqa< z#vrJkBKYm<{I-o5)4g;UOp;dRDNtb{;l4$19Yl)22|{88%3DxijW+Jxwt4e5Oq(&Z zB06YJ@3Ap{Ik=1$*yl(O0^I2iopYoFE-$`k-bqA%ijv=-r9VI!fc`&n{ENROmLEe$ zZvNAU_s^ZY$mJqzCCUaAsAE@|c8C~H2)09F_p}v;*MD9?bGfh>0A5q#M8}9wotJ*> zhqnZ6sLp>meDYroD{ykj>eDUn$YW3p zV+>OkjbnlU)1|?Mp@6EHi(G2(m;|+3*#G=RJAfQCR>LM%WfIKH7MS%$c+p-sT zfZTx0w)gF8!2QfX~!X|BseAZut?`y=54x&xvKt2BT$-Ayo^X62+t?FR!kS zLP^*71Rp>eJOnv}6&T<%YfdsR$#6VU-^ZOeW=AzP;K9~II<0qn`{MDL^8Tjjw+a7M za~tTIb>^Pc!d`dbbcO6S=+59(y@sP`}7^Q=IB>eht9 zkNT~=@|_@iLWZ@AxSBN;)(uG3!U+rv3`|T>sR&y4;NAMYg#LuRelUu2!bmaZs-OXP zbv8V<^nemL&FP%dklP5bcm7xeoRKk-h5TGj2CkU59p~6rvjLVY5RRxoO@zckbqMmto*QQ>GDJ$@zag;lPY58ibthzqaHCX!Cyqk~yQFRsoPkxx3(}}4D(dPg zE|hNM=%H57T`A2$8_6?q_$Lu#jE~gUr41$6l@;G>|9Ma^qQMKl`%9z=d8cDDqzTcp zPkD!F&Jq4ue6+wm-{3ez``>Fu0s}Xpx_<+>*`Z*iu={h%3mlvws_^pb<9Skw{sBAcgS9jd$P{fqzggVEh7 zjy<3cc}S@D{9e%RZ^6F|K+9uKDX%91$@mO+7T^ zK%=E?-85Wv-QDv$oBu&i(8OjSuK7^x{9l2SGgK{gOom=rFl7>E>f=ZpVs-_)pA zOoP2lEtV6HEqQYD%wl6UlbJ7l@+I-^B?LPTkC-gr>bHbP2e%4VE>56 z5*GyUT;y-^tOVBiD0jEPUftz%Paw|*W&YsM$OP)ljec47?!L-j;NY>B#_V7`MH4f zk9U-rJ*-u>3!yKT^7zRZLh_3d_rX#(e6um*hhE|LV(_)gEjaJv&Oj46<6WZ!>%- zun?B*C?LnN#UTjToQ|Wt^P*5xp$pPj?rU-LqDa^fLZ*lW+zj)QGBPGIGP=&u<0>Z^ zVT@%uGVvC2X&hQkG47oS3BSL(j>EPl?RpdDY3>UpV}uDqkcf_R%lK7F5)=$3e!zH;5{It%@1FkKGzVj=mir*J)uxULPp00%0fAOr?nM_ z(%4)Ehq(i0oXnCe&L->_x~Iw>TORA8_Y@(_H#E{3(|+<_*>XAVoAoKTDRdfMmz3_)@S!3> z-h%n$F7LZ=;=5$3G^NQz%;qhRB~V=B`ELyr43vf{-JDPo6X0{!2Mrf8GZ=`r1f7&I zkRkHbN>g!Xqr(%bGli2Ly=#pbml)SqkSGi|%d5?Cans<)Uf|&%y2eqKvKu~?eXWsarll-mZtp5JpT^vEqaXhfXji-GwwkeTRz1 zl58uKT0(s!>Icj6RR3tgJC^47kwgyws{h2rmlTWJGa4|Rkd5Z*a#)e~C7Dy6;}Qv( zh?tTV0NVdd&==8%IU^%Qlo+ebyk5Pt+!?-J+ge{&b@{>I7?Xgv=WJ%&~5Fem%r! znc4wWOb*7#Zo^q7n$i*91-nE|u!hRX+o@o{{O29yZW1qm1JGmB2kd0+ZvGn8!!dVx zOUhpC>TjpsYGY``l2HCAmT8KBSn15a{uH_F{yAvQpOds}xqNA2u$on?9&~7dhoO`@ zHdx&O2ZyKI$`lq~0r-_kn2MV^>bphM9_yk!Qz_J%>EhGsZh5$V)q-{BceBzvp^}!e zHw!>jE~=3w%f^EoKT_D`pEG|-=6Xs+={NEjqF;%0sspdu9LeHE5s{RUqe9m?dsxtQ zZrcf4m_Ym4V^r2C0$IYSfwt_~g&u@bKGb~nr{Nfqi9zx5`@3OjZUpAHi#Qr7mg3eE z7Bf$}5zg?##zV*Ua=F-V@^!RNPn8iqX!u>6}674Gnr28V`j}-%S!X793-5<2YPtZ4CkXfVjd0 zfR}t9w{Ku*M8Zu1nP_h^d|mUst1G1abIVz{5?c@k|NA(JNg?#tJF@{9n4j6CU7>|} zR#vzKh{$%CTWp2jHv!Bzs)aV0t$I#9wpvWq9x)DpxlFse(vTYK{>&AzN$7Xc-m}C1$i)@kR$E-SM^eJYh0f!T@38aVLS9OBnjP`(ztCi8RTDWi0Wc{VZUGu(C2-rAEKrG7~_mXw>v! zX?QCFL8Rt?&B08Z?l4(cmK1WFQG}btYTyf!k*8DB>Sxd;E@e<|Qfq03a#BsMBH;1# z%}yXJ85$cJ&B_~NvOrp*YJ`O5&DRk3MrWwUA(?QbE>F09Qdl>Fa9OX%?v&T9MlbD z?4}fx-@3q86qU-IO{**U{J87*%Jo%-)y}|_LxA@#7S%1cwd^?NdiuyE(-^VLi%;YM3oafO z-NbJjsYxydKo2z_3&34CD9LOHRufFSDsJh|*YtyOR>c^q>yS-*{p|$oYMLrG&lX_I z^DLw!u`N6)?V>*U?+w*Rln^(kCo}-cWp~rCkwV0jgIz%P>UGpC$rk@RM%!k5d z_A&}S(-XY-g?@#Bu+fy)Elncvq4r^QyXsh(v86>%6sNn}3Btf(JdgTJDP$KXCNU&D zx=j4F3}1DYxge2NtW2pa4sCO0%JfCi>FCLMG!C>SHZ85zW<*Rc=ATr=yjH(~Nmd4| z+}snk_XvE$_Nbz>>S%09ls{r;1JrZhhLny-me|JY-YC!Q9KI>3k7$~NhAp&ee@TBc z(6{(D)MmwysLPGb7kmNlR(O06UZ=p(C2>H-R~21hZs@W;xpnayH3=l1907~{9SrKFs=Gq z7pe|*`^c<~&?6W?86$pfgQ0!s_)?@L^6=W-D~|_H5f{##e(8RXRNal;D}pXMX2hYr zPK`ZEgdK5{_VY&aI!6{0#&+tzPrx3R_ukni z^2%6h_L^VFH1up1p>j7yl8Q9-6x_w!O{=;p=^a?+=ouL4x+~IdzatfgY74CnO7wADAgzOimk+7da%Ip+aIobsTdxHO* zSuD&9xzq)&&v-}39&YiMJJv7<4xd#{F4rBL^*YpiS7}?muGbZ^+|?bLso1FCXvAEv zu%c$K<1hH^$cO4dTljEm+o-r&`I>z-g7DBsxT5izf~UoJnj)s{VpWJ<+}F;gkeuzW zs^Q56A6b)}=mp^Lny2jaPJBVU^&R1veBE}GWBky#3q0l< z;n^fW5D5Ho3wrkBxZ*g~pnLSqQpcY*$+p&U-TqQgeEjp3aU*SU+O=jRHrVKTyj?~^ zqOJXpLw}cUP@^5i z>_jlD7+zq9u88nSG0*5l!P9|pY9)J2Si#{3`ZSxC#F^XNGl)gDkY){0m0J7~V9o=> zLMCnr!~!v4iNs=4(9)htQ^N5Lbat&Mw2@a#lkj^LZ4s-ircqIoUt@@bbA4cBV{EzM z7QW+hz1+v%TUsYRvuL z?x@tf;VruoZt&TD?8g$uh2(Yd#?4d*2!!?h>+dCU(c|89Zl&52Dx5%w0Uv+cQe(g<5ePfstYAAJ%&Fan^Qu?vD( zgEKY$7#LUZp!oMM{C+Rk&&e15W9pN01XuWL(i4qK!!xP=oB6R|_;^r!2-dJ~oEyY( z4>P$8zszuj=pk)kc(j)C2a>98kOH<28K~PS<f4VnxojX}dBr9q;GoJYCMm9yqI(OE^ijsu-xjvbqCq9jmR&UH-%Z$`-onlz8S)Rw zMIn*3HuN8d2P(0K&*D3Jc$EVf0i>~94Gy%#?Cb`X5nBG`ju2(52zc`Ax~{&*9w6FEs?^&9 zqIYM@t`w9e$bP&2Uaif~9Nqr0ywDm>DE!WA%`I~Tjc8+uvnfE<;D|!zJK#79@HP4c zY?YdKL|KAl{&hK#6lvg1qsGreQ@Sh^l^l6;Uzx>k-qu75IS^7!OQCPFVPI}xqPekq zX_6CZkggt;?9kNjR5{^Pt*S}J8io!FQ7g{S8-YRyp&k!%Q}-?L5tDKpqbP%zXENhOKU4Szh7%Mys_~{@iH1@cwv`8P1rIo{#IDP*EhKla+tiRz|4{XG zm)>xwL^xd}Gs`KY)KDNaU5h?pVRXtEn> z=bnN;x93;X@a`kct#GWV6Rs_Xodz1K&7Xg1tg&2EW4*8vc7!;C$Et|6(yj>XlMyO0 zeRa;7^|R)}pS}syIZY6V3WXNtR(oRKpEW_mLX)8+1#p-MiZy&L1mKus(9vd?lr;ae zPC1GI{qkX2PnKD1SpPYy7j=Bfj+2mkV2U8)kClM_T>b4ykePYGFZte1gR+CDeA;mk z(b5R=DA~or-yNzcjLv|rBIOV5j9HG#?_E5J>Ji~@7T=~_(TgmcS!8OaFGXJgTgfmXH2=(XS)iDVd-mwNyMe9qt9#6U`Gf2b)BYY zx%hB@P7T#p=9Y!4XgVs*K>{?wtFg6)Gfd{w)7Dn>4_|IfX8g>K9PgxN=mmOE-z2+f zEFweQssTmIDxpy!`NSGHI6s@#l0Y1o-Ddv`<_4&R&Ho<&WP*m2H%I}xKmESlpA0?end?F zKJlj4LmAwUd*%JG6U&|fSHNa)5l{LfxDu}8U&LD1feG^QV#dK@xQU#aJ|CvSTf#L^ z4X47nu!OXx8`C$ZZ%aQ2KY>mBZ_@j~2tJsFpPlJX#J{9p#|fAUKY%;nEpnTF10=DR zRxEipoCkOE%>qf!OMitUh`@z71_8#vPO_a#VC!?>1L7f<@k6kcAE#649at&=%`hMC zgw3Qc8No%wsp&E4o#4VgF2YvtfOgo3pAPs1yh;@Dv-E@M&%g`4VH94^diXur&Ufy+ zs-JN);BhlM}NGw`a?ALc54i?zBDawHl%LNp<$oaz#?J(yMYu-1Uc6EI zu(PpqN9P|p|CO#xUk|4vKU{^Q{{h^CSGEbBhgb0P7Q915VkBn#gh_-z>guL$pl!yWxtOg zIfO^hs}r7tJ;GMJw%=pR7fA}ag8NibpdAlfwbS{>=;Y9~r zOPcvRrBAt6;Cy&qFul6k(^9uM{HaUx`v>D$2BtxVkr^9BAOjV2TGh=Tzj2Nhl%i#Lw+0Cj{i6vvG}|fl}=E zuwI(Tb-+B)Oi=0o2*2$d4^z?)!5!&&aBliGC_#C!Jbf9qxCY*XTVM^juJck@g4*dT zxVBFfhjY)1!_y^P8}|w~fm?mx{l(di5f6NfpPyr7khlffgg?Uss86p<{|ULf7fRSW zU?!e^cjHxlf<27nw?kFuY24cMaDEA1?^|$s`lsm-F~I!vA{Yx>;U}^PGvox`;}rQL zUhkzahZ~<>#?R?I8|UE`oP{LL*7+!ZZV;9V*9c$2N?h}+QQq8#>*f($IdtsbeB(=#c(m zU4E>O*pdDsnIA9eW0VYdX{bPnqGbF;uha272ePitV9~egIX#{dl1`JQkN!=<3w0bx znyh_@S6TW~PXY&%prcXuC7SElpj7@4Tk5%*`Gb$*ngbe=|~O9Z>r0L3WdYq&~!9m&{b zK_Zm*?$@tucw|y!Tv>C!1^leCOZW>TEoGPXTOMgGySblQ(UE@h31e+A94_e7n`%bA z!_htqbT!2;r8?{?E{_<&sZ`?}g0nhOUjCd??NF5JawSqNxGSX&?!k@YL?Z4Xfz8~5 z;O8D&=MKG?=pcQQ28Ywt>UKLsDDEKD?Zi-tsq2egB&NV-)JOf??d^SA;&ko|w%YP| zGTs{J;~m`iq@~#9c85aYa7|5RWl<5<{$A1rPKP6rsHiX+4Tf^$qgHqUZ)OLlB=urx z%VPCPl~Xs9n?WKqNlU#rR=iTYMHEDD?K3yByU;vl&)%hb&}TOw``On$Qiqv|J$v^+ z{a#GpTUSqsx^Fl8Yu;_Oxoel3OB2h@m+!D3D#|@WE>4~jsaEU-v1lwm#!I%i*2te#SgmLdC<`F2)SBAbbQOS!^om)qsG#tY-|zJ03u zRU^yM1UVirw7T3u&gqb3Cof4(hs$m6*ROA%cp>Rr{_v%S4bHx&E?B&5a`TEa*Dbhv z>II(7>YTy`Pp4xMh~209zcA6c~h!g03A zlPzOr{w%Oyscqkv`S#d?+a_)KO21L+rLJh6dRc6r%XC-E%nQq?K)5jdoOG}FBCb<6 z_@NLbc@5)p!z%A8A2(m;^ZTh;%Hr`lJRYyd@3eS*6$#r`?p~A-b3ozlO&WQh*UJ;X z#}n&C(?ghE%H7)@GX}PDcR~UsPX%}9y8K7`N;K}oxCNWkQycgBea@IdWlS?NiTXL? zls)_Q&>O{8HHN#E^zzcgD|~3X#ePlHP@-At;X`F%u<3Tp|+HXbXWuMgqChImrdCLe^N@E7U>ew8w0E^zceO zp3<1nOB9~qy|1dT$GflYuEakoRQmL*s&wJ~jONElKE0t;mGpk{q>7M3b4C} zTz}nKQahw+%HW~s`25?xd7Hd<&kr$UIcD^yiNl(v2vhEU{i$u6pM9Dk8FA~)aj8qIiy>{#}rmCI-bwX{q@fmX<5a2>m&P7rw_e!>BNRhXfLHl2&-0I3?_SBe+xI!W)hM>>aT~!oUI!MLPG>##M&g{6 zu97NA#*b4aI6T zDZRK}DyKK71n9l1T4%VIC+M$-d-=1>9qG^3t134M17EDSSXf~9dXtF-{+hHH4BRA( zIb`O{kJ_@MrX2CDkrRtPH*$LvtbGb`nHN^!`gW?^J}O}H2Nqs()z&Fvp6@)J>>_V( z-L$%G${)Yp_v$B|fA7@Mai51fdw*dguD>QW?hu@-=f2eO=ZdnVKi41_z7)y1^>}>c zIIlw23X{8g>-N;CyX)$pY%gwnd-1+KK{t1U%yS)l)!Icoc*+u0m6cUxWjM5$HPSa? zt+Ga(5i3VJmy%l^A-8lc-9v6$L;W?K=hBfp()lL226Zz7%%&sRV8DaI&!i4AE*U3$ z9mf#^sRIMYV+5ocx&K%^yDr9)*nQaZea8E5$64Cjymz;XBf~v;4_m%!X8lsWk|jdl z3ZQ&dwi5?cg=~eQudrXg>Ww?col#lakKeg->5cI*-Wg}&Xc|Zdw}3kbcj19-Uc5`V zB|JBVjKP6Kf%Az=u#i_+auc1A-OcLzP&Q^yML8_R+tLn6yelY#>22v7aW-&8q+`Pd ztgsm`bU9ul57n$y4@WPlE^{G|gK!^~xldr%@_92`7dZ5FT?5Y!d-KknJDDvr{Q*~t zJjZ8dY!mS5H`^Vx9Pa+fu%q^eJmL6z`Nw&VzW|7XzQRectbzX!xDSyRAKri`&~=x1 z=)Q!~FT+>M#nMDGlXC=F4%ddBU%ztTfo4+r1Hme8N1o3kopc0i zxy-8-zMvpFf+mw2k5L~o{-%MXmom8C3JOgDm!cq5Xd09ubMHj56R((E^IGZKE$zjY zB-+yZxKdc)lcd*ZlxUMGO#xL=D5Ys#P3~_qd729rySR*Ud3}|g8?h$3hL7zW(qzrS-&b| zRTQOzB-c;2nvKRxKxZ;xVBKVE3(!NAf-QxCJ^;=Y1zol%WQuN_46B0lc%*)gmyTy2 zq%_#Plm)X>L7JCBbBY_1N{(w>FNM8o00mMz7dHMfy$f9EZ0IcL*mo@mx~8Tk|5}p+ z&mHR*nM{I@9Y6$j0I5$hXud2>TKM997ql-Myx_(A zU%L3#O%Gpo*~1TCaoH)&+>1mY10S8Xt~32=XJ_Y=kKO(Rxx4d+pMFN>lLeogeLeE{ zTX>xR8hOk>%#_2{8M>}K7ck^z3vvNNW-huWB^jB_)ARd6;Y#in?hc*ss6g}}i5#yN ziNcZR3~Y7{^gaQh#{xVvGx>~xkCRr$t^vlbX2z~KTS+fv#vI2n?(->P5;X>qwq>Sm zi6ks0MNae@H%Em zs+v4@yEMr}7nIe?0-q%Hs*0hYLk!Y|>ZS#PL7E#3m@y})&?^jfa9eOp2DdvLQmq^p z4$-sbOLRbGJE4p+SfZZN9hGzmb6p)OHXFw}OzJIGF4xblq|s*QCIuZdjkev6O;dqk zG;)*NbRT51eX!o>s;2$X-dJbJ5d*{l(iU->v_*bK_iR8Or8FrM%?p*;=1Xjs*l)0H zwY}$i&;OZE*=Bsg&IJvsPLiGr_#A1Q5a|L)ry=5(1 zq|Nl$nV|BQtxnub@%))lOh`^UF{3!n>|)s^o7DT&fSRWURq2=M&p^h7szYCoF1KE5 zngBJ6^KjMDTWj*mvWW}g*1UcZr#MXA)Q9?nwbojNQ%d(~XCz*Y48^p7Xg=E{5JxO|hJpmwe$s`nvkfNx;WKk?ZgQ3V7 z3<$xZfLLUTniP*04^m;3F2b-Jr(8md$IGaU#|<4nV5>#_8uu~0m_1LcPuptM9f?Zn zD1VfTCYNcL>3Wke%zB3P0zW_AwMbpynC&{>bg|=lQ=8)k|4&Q?qhc}(GQs``Wd!;< z;ub;;xF%F{6~!rds7CDN<|p+yIuVCwvK_z|wr+f3>$VNpTBd~;hq&I7>csfW;yZ1#xwQj)4Gj{C^t9o~k+p^4+U|LO1 z`0q_LckKcDcGG3FC!+*sSgnh_+Ql%Bs}tF%iHak#lrj+zB19FF#lVzMizQGbWEEAf z#}jZejtVf2VrraZDGI<8-E_DaY4$j3jw=(&Y+XJ5Ul^TU^al_uAC?pU6>`m*~S_iz_oKI`ghuMKZ_ zW?uX3X?K+de{u8n&VRm7tw27MfGTPr1~72}%6VHsNq?1@_VtrZQg#Cob32KY3+TJ{ zmRvy0?JZI+pzqpkasgSFEv(B0KM{bSul~-9fx<-vAM<3u+ zF3jOUA1l*37}&j$*5NjoJIG4LdVi&754y|krF%l1`o79GIdyN{=XKWFT9VbL1(a;e z>_XT>HxnLTZMC>hyp%Ln)$;izwY-p*SH~V%AtPd$q(ey>Yn9eG~`cP)4K_nv-w zeP>@X?I--kZ%+A1=lv+Tf3R;M3IuxUjffB7zACd5i9N?7wp_rjWbd{u7qBbYTWZ6= zCc4lwLU0ohoK+K@MiMX^f=*|^MmJfbMG%4klbL|*!6OJeLbCu9m~{K20*@|ayo`N2 zP{>gk*T=@TX$zyHeHZ7o<*l~=)c&OMvhtc=r?-2|#XjC{aN6wl=gby|+3qk~Oep7) zcG`8)d>`tYW=qmZvcq`7B9IrU48+YaX{7_QPE)B_?iN*0|7#iMVKU5vT7>H1JUJQW zSrxWzC4Ip{et;!w+RYmdCd)zx%CZBbSu@r0P=2vFXhuoXjKY8Sa$RXcM7DzN8|Hd_ zxma%8jQb?dbdhFL}( z@{2XIZ*~`NDAVky=kh4m!0C{wy=BrboOdj`di`T7&sf>(;hVWv_B}E7+FQ31-LjkZ z{$?L(Roiad@xYz!W9wbq-yZF}U~1>*e|YBB_FZ`SW02FFxNqh`G5M(5&Ka_h5Sd1J z;_nqqnuy7S$0@&{(A9<-RQr3T$9WgR!$H{DFva%UBzoiFWFpUSaN^B#3a zGi9NYgk1-9AYWxDeN*XgbgysUf4WtGG99u{UqfHWye z4|9GRim(hitZ^m|2j(X)(z9p>-7#~6By{hB4Y33GC)SOBV%_*B)}k{0Q3vrd?In23OXoKDc= zra}c%IY$J&Vrt=ej^38B>{QEhK8vnJ|35GmxpErE*Q2+XT#34!*PLCz)M~V}Zp_|2 zr>UP)orhW*=xG;E2$`cTu1EiRJ+*7HwMN)ltE$ghP$b|bIoi)=tDeDcSlPK}bU(`` z{_20+Abj=M${%#vI=}9C?J@E(dFC$QVFIoiFRmIlM4_B}djGmvuYf;TO1Cl8Z@5XN zrM5^=5_<)0rXbz$nUP}eMrNZ(Sg3`LE*(owA)x`5Y4Pwm^Btcn#dj?^zQC!_;!f70 zlPx*tep|$Wh7P(1?O|rmjCS?}8;Fz}h?E(~Zf5teAq6ZZ88s7*THV~zv+r746ehVJ1H=>cc}rm_>SgP2=1Q4Z@cSiOFD4djCMm+ig49hcFe^Br zPj9lySnj9{m|}NylLp;I16im$zpWiz3`i1kayJ7CU75zu8 znsC*c$F9Eer@X)D_67I7`qq7Oe^}Ie$BM0KJmzn|8%={h4imtW*M$2Tu}iSP2@O5csa zJ6#X^w)#GHy&wKOY#->l$Mu+t?_V@q;(7(gny1loRe*LvUL2R%4cF6dLk)%9ct6yQ z_d_|}r(seH#%|?|>Cd~$8M~D;lA5ITz{A$6-gK|sfO~Cj6~uC@AeLJNu@-CBDzGN4 zoOM;=fK`B7V0Hy$`M>MPwSNoT0)=?Cil%q1i-e_c&i=5JG&ivgP%xS^8z9bk*P)8- zgx#$WT^nHLI5mjt+ozE3eCPu*zRk)ki*dr(JM#c*$z!g|W=yzzTtCup%Q+jrA@b9= z?78%kzdi8itK4sYvh1SvhcCPQelkJ5GkGD={pBs9-r4zX=ifU&=v?>n zZG4{}ZQOC!O5FaCzc&Gp>xDRbLm~5ADU7;;B6v^cutN9f)v9|_g?OKj^nn8;L{VYF7q5_cjlo3_D`R5)UgrN;)a^pPef+z zktB|=Co&v+B2z|fwS+#6K_~S8riT9wb^KXt_WfuLaOr&jViRi2GMKqGI-Z6+)KI?Y zX7>7~E3EI9E_MqGxo|f{SQ5H4Vnz97w#ru-HpXI-(fG@3-MzU)PRS*b{fUwxr$@VT zM&F!@Wz4Db218C~&js}6+%R$}awg@8$;50?nF{;&_1VA|OmcB_LvLbxG22%~M$6Q4 zb)Ig%e!9AXU!^`PJ}qrmKU0l5v58FP#;Nm-DfJ)9KTQ8H>jgzH31;4C(2Ihgn9Mp! zmK6-^Bt<3w^d%GvGbo2;#erEI&(ky~P25N9MEWA$Moc@fyaFf~b&Rg1?_+Q{(tCKqTilnkgrvrlPMyC)i z^K_e;dXZzJq>4oO2yc(@@j^-FxvD>Kjc@#H-;eHph5YS~;rW3oar0Nh$=1%H+!V5U z(}g$Ph%0I}o@PHnUbZs*>CMa&GGwr15q&-+2*ab3qjRGz`fK&l*}n6|CHfZQHR3f! zsnDh8J%zIZ!Q$egA_(LK@dky0K`ZDyShusQuBV%9 z_n`b%SY5+pPh3*yK~m~Szn_fJZA-GzElZLpBS<9Xj_V}H;(aJ8j~TX5Cy~R@kcoO?BjE`ld?eexyM)Q8P{e4QszmB zfj#|bP7Ijp>3Xu!T(^%JeokZYF&XP)2Kx=&=?w%9U2iK! z*|lMj%|;+&yapY~%jgGOw*wM=ENYE(vm=_hD2Y^N?AY-r3cJc`x}DJQYA(L!w=Hw$ zU3beFt-o5?`8_i5s+vut4TYi7 zGz*r%vb&=1=yYUk36f{mS@e&IEM4sI?pkkfN>BQd_H3 z+Im!5m0yciZK=GLQU$VkpXZsG%?9lM*Z1=e?96v&XLo0w=X+mIBVTVdCR0_Dw65UX zP*?0??vje@?Yr!6F%RqCNs318km3*fl8H0 zRI-wpHO>Or6xMO#RS|#EBmSgEyh)e4BH5G(F<+*KT9-&MMT}Kmu*l7I%N6JZ;<+RV zSFRasMcfp(0*NGITX$8$kuhIm4bxQFL?6@Ay8_#Z0Ss(gp)&4V*~Dk!I)q__XKQQH zcuwqeYYR)Vk+4ulp`cB~I@|l=DaD_EaIpB+w>QHxUi=drHtvPi7r*iJUlv{Z;kw8E zjL^tWkNpf@_PY<@oUQv`8NTVhM~k0+{kh`DcRo*~`1i03TY%}_!1LltaXVE7&k$Ye zw9HBah>;;wG&FYWQ&ANe7ztLMN#Q_w~KE6}Q6t*S|kOnbKYDz__CarRe=ukCrVSbX~YmS;zgobd4z zAO5?slH@TwiV=rY94O$Rn<>O|m7*vJfMLm$$x@{PM1kOV(6rlx+00ofIi(>vuCWp- z$#~x{Ut(s`{{u6Vr2oLx)Nu}_EkJ-G~{uM{^MvIa6LN2G8i%vJLyQAFa-9ABk&aS?(q-Mmi`&! z>Bm0XzZBo;rI^3|81vT*e3CwUJP}NU(Be9Ht{8welTL$bJAyKx5>W;iB6kZRAF0%t z>PlXMFk6>N525sQ8KtL7l&*E9Qw)R0ux>GpYaE~>LzO}=!0`^n6si>Ju|A&rgY^CO;kWYtZFkdo>fO%!7u<~ zbz9^RCCJV}U7Lh8NU~ln-ah^!I{M;^{XDm;{|U6<=tR`hKNUaC3z#w8jK`dTGiU@1 z`OnYz01~``U@&l+=9zI{lm;C5Fus&iU4hA9pmgamcRuK)9=xNA@H#}&YkJ0k-{9_RGw!~k(%nbnZdy*GX11lc4cul9(>vBz?gg8`7Qi+E2h0HPgF}F` zQ~2Ztz;JH3lgv%jo3p?BbM_B^&W<>yYsAqxdz5{1ewna7W6`{AYq6zPnBTpsqrYSV zkvJ$-ol5pk4`z^w#icS7mj+Q>%F6P8K?^-yVpbvEH1hb0AAD3?u|NS=&n0IZ8c0zJ zsQ^p`oWP90;s6_fSwQnNTp9a`kI#Sdgvk_phd*2g%|iE)l68O$P^#G+Jeb2Hg*no8 zJc^RRQdx?hk&abvXllXeQF!2Jpd>qPv#|1l$;<0sod2`0{%kMY6n$#V8CTrMe0Dt6 z_v_{F5t)iDL+%{HGtg{0u9ZwpdtQ{r*2$yz(efmDF0+n#ixIAp-)7#%suSV*ROPGZ z?q=`ge!zYraxx1?v2U`ZPGr9$+0|`Kifpht-lOJia<&H_i{73k`wF_>-D8Kz`S+YN zV)!>RnNvh57CVJ)j7Cq#W}hI*k|=Ww%ceLv$Z_}xb0j{9nUX964zUoE7N%R7K?($H z9~$cz%^cjsZQ*uv`#F|7OC;x%W&x(K$-hNlgg&&c=Ra~NESi+m|EtOO*`RXt6mciy z2KCYYLia)95hH@7gRq~D4)Vghh4>UmCKKLAY2aTF%?_~xPT3NjHDya;(1SpX^goQ5 z@A{U>!J!`2B13z~iC};?b*oJ@b+b((kZfX-_HyUv=LQLjBnwzl4IblRu{=v&HHkbX z>i2epF&NJCWC|2JFV+O}tP{+WDcPREU?@LGnz{Mpn_>5=LJkloMgo5&&$A4k3d%j% zA>6YSi|SU7hG8|t{{`!T7ttS}(El*{>Hz3JatJfh2J}|{kB&ctKK!J}y12+T(k%JM z-Mn##u;k)^m?Hih^yGE{mAES7Paad!>6rgo$31eAZNYz;ZSi5jRcVR(1{hy`bxF>b z4k7$hzr#H0pHxGA2ms!|Phc{xG!PpVm!TsJ49RuCti=^_qOMs~ox+?H102aftS69a zlealTWh4NRC@DG+B_u06M!73~&|A(&C_+ zlI=EwZa8cOLmk^Ocra;}4f!?-&@XuobpacgsLRnj^_%yni+(L2al72%ro+_a!zATC zOrcDYxI~a}ZUV>Ct^@k&oRz{4n#;4C0W5j zQrAr~PmArfHnMl2r?B`Kxs6MSeQ@M>9_q%Jw-s5dE4*@YilhYG$H_2HcX z!D(_?cmq&bq!L96q>Pm&H2YB3&J?$pTgzbyjh^bUh#nLpiT{O;Xg`%5#Gi?e;$?>i z3Iu;UhE#^d&4bj>x&8*~=EOTJYfA3Sc|QRvRy@RkHvt-Wle#gdY*F!h_2rC88~A*y zE|WLC{H51-ROj_!)ij4`M|)mxX{X@!;rOhVr<9w&s=I)>2?>q>GG7|u=NxHhf(*~` zN&dOo0JhL0quXIMR>xp1v>t+KxUe}K8wJmU+;hdQ&Bb}#u4A8l{mdEPW{w}7$i8xH z6ubWz;RxTsGOmi)jR;YyWTL*~UG#mL+q!JUS4_pI8m3`vEU&{}JiFGiPM;&Hbc?XQ zB#2lHiAZ1=QDPC21d(O18gZViBh@>(ifWnDl$@Px5LZZ?` z>pQ<>(5_z|FJi2@nY{^P%-UmXNk2JkX}`<84s?)!u1+*1Zor@!3??Fp1k0LiP>CoB z_UXtD{Ux1=M4}0ls&K67f$0$^K98Fx%{Av(=LHr-&Wm=%=O*roJdDg(CBxX2iWC~6 zYlg~@B;@OwJGw&IlmLaFmK8?`7#GR(3ckWEkhBLfsRGd^$-D}FpjjXjPMNY0Ut0ky z3}0j#J_;En%iPEk%qi7PcqV$F3-D!pH1{Zy9rubfYpYwwKd=Zcsm^Z3Y_>%Z*j*9uN8Ma^($EM)*oTw z+K<2fo8nvO*YHyK-4~1B|I_=$P1}D37yPvN@8WB)4JLY^@{QsLuE&$@$F#12C>-Hx zv`ef@f@q34CAiRB7-SW-5;JlTiMswGd&q=*Y07d>mz@=#!_(tNIrJ`(MiNBR6N-l& zn~akfPsJhrACGGOh-?0cYb7N``+s#jo$Q8-mFp^l(S>ezO1$at{czOSC9aIEx=I~T zx=tKW(JyyEwa_A1R9$Uhxa7=44fjo5ao_xZ6kjgh4zGXydxf({-dena+ojt}c3k>g zvA_R^47~fMMYn`B;&Yxia1ZwnOg|w|j~F@w_n(*Dm_@N@dk85>wu(4yf>psC&8RiY}M)(8pLF9vIJjvxi zBR7s?3*3Ft2eNNu*-W@G+ZN7eCr2kICsa+Sos#Vm=UHex~k@;=GH9ZmWD3L zUZ1@uc~AC_(LZHlN)(2$EZUaH1Cn9eoXE4$V6>4N%dv7UVRM4NVB8CP0Q1 zSl97ojxa*{`0Qwp<}Jbi+Ou`+zt*qHfA71G{oo#!P_H~@o z*nIv;rmoE*#}##Lrnfh|J%0ZWJr!B^eSE#??aBMjWDI2Vvyx|}W+{u3 zmnK(9*XY+8x5>8~4{A>vea1)nUkwxU@|0x+Ez7VBRk9PPIv$pJo7566PiVX(nA0MJMH+mDpYMzh!s)f7DHCiD7-RpWTx{u>(;r_Tv_+Q?7??Z+wt%qLo8# z;Sg7<<1G6l3Ik20mqo|O8|GNc9!pdR*iCg<9cv=-Sl+_Aij7~olgyjgL^f-x@T;VJ z%uj5SV#F2fC@W$y01d2lz#2*z9)ETHi6?|+BjHG(mKlLChpnaDmL{uJKZ5Su^XhfK z{{7VYIcEU^M<;+1=`3uJsC>jQPfQ#p}9^;$NK(o9`uw9!^O zJ~N?dL8_~EPG*I&T)RYH8e9^+R=G~Q&bYz6GJQp69doC0hjyp&HS@OgEt&hY2aE?o zm7du=ygF+qvT-Tf0J8uz#BH`^WEL#JtVbJuZQ_muN@T*?@XERj%y40@6e{H^hf9@} zVTP(sBy3P{;~QjGpjAjs2i<=OXLu&9YYJDLtf)+g0?#sthnaK@KF4#FiQ#dF(9peD z_c#~^!)XAB8Wbi>!5MHdTnRTo9`?a4jyk+D6$qR@hx{-{NLnLDNG=+T zPz#p#S+3v%ydPx{{?{l2??)Mg|0T+R`8S6uF|q-=T#eL+uINd*sZ~4q6huuVuNcDZ z(2MJQQRE zK;&s_1#DDM{@T*%l`YYp3V~t;duK7hY0o};^q*wxBrN|YlFB!91|2(Ym|wo;GbI)S z9~f_f$+ zH>u6)V)YL34rznBTRo&IDRqX5Sfq%^OJN=rnz1c@|URG*(A!nNWC5g$Y7XvnG0pNHVRXd^<1oUu~e3=TDOi@6Ql zZtf7raee6a9%b=Ut`n=9RELvS)O7nX#A64eZchd;8;>O8xlXF!pd)G)2asS6*`s+dv~$SosvLB6=tKw_9&%__&4V4wJ2iB6=B? z83X;LE0`fy`1ocF=6|w$$pc=7crHTwe3HQ3o`9*o9Za&&Yum}E`1Cq?Ihxz;>;Zq$58e8@mnlmu{0y7gpas|n+= zT&Rs}Q^#}T)hVIb+-!AW=tAy7^^(wPZnb)S$lyZ6FKlD=$v||>J6*R;go;CgeU%K$ zaflZ%_Q_c0NSbaKYA|5iWSO-nw&*%~I1o*dy=q%z?<@$3QVMVgr635B_8pukR)(U% zP$+7vl2jS8F=$(=VWdneXquKSsbVz58J4L6gl~ysqNZU;5=H}jPf^>pEFi`sk+^x9 z1ZRR29Dc2#&H~$#*U`im>1wFgD50=xvIoz>(WZVrImnpLg{# zKRP;*E!>@2GU?c39!H;yDJ#ItqdKkv%rZy0;lsUMRw-{3>GYtQOQf{5bF09(bU|2y7c0*tcY-h%ZW27&)TEevEo>TeB+toY_=dSQWog5?Tg?NcDZ#6x3u}mB zvQG+Ava%h6 zHGzPkYZ^37&9Val1y38&6h;lm3gk@`kmNuh1*9MV5`#1?r7}TPWw3Z*7=!|vrm8?} z3c(Pjw3OLYjz9=QL#ec!Y_V~BqDq|D zsfi?Jg*;C=F;wB8ob#zxLhVKMuTY*4{Qu{)00P18hCs*qDmNM!896FNPQM7c1}aETIox6eOOPct(;{l1-bEm7pvu zJi|*2F;TDT8kgk}Ru%5LHQKcVYI+3>F2`apZj;B zOPQ&~iEGweu>o#A-qZgLVjDDIyJ`!;O*qTd(aQAs6PsFW>#}u1t%hc`oe3w2XG)AL zDw3xwJGu&V4Jwr?R!%B;%pLnXI{Wu{oH6$di{}qQR+3p!ma`SrZS^wzLWXh*vREx) z<$5LA2FZqCPY>R+1d9UXEdB^rDey>l zFRNc@U*xRzHXX8NQ@SnEQXPz**Km0QYDhNeo%+4{Mtwl%HtL)8PjyCr&SyJ2yL8p- zY50lXt z3ZV2XU_zbTl4O*I^Ud?YY37iOfzQi0<#8{3rE~$Fl7FT;P24e_bM5bLAUe$wj*upK zzoc?IO}yP?JkumsO(D0PrZ=4SZTWN`TIlF?4(ZC1$~I>=XSqD_o)a%mwr}7~7kJLM zIeNOXBl9$BU0K`KoZp>CoANLpA)PSDmq$cDDm}#mP?mi{Ajk~mN^rjxoz6b(}KDdPiN%IhMzLOJKBBM6zBZ2MqG2~*lnkb zf$!EQ&HAZLXD(hi{gkT-9k0W5OfoG@P=Rbp$JawnHPYNDZUV=3R&A+5RaG^~*5v8Q zl~o(6__2YGa7X;?@Y(T#SkUGfh48uY<>CtMV&k&#W%1orZ>#S_-iiG=@b}2yV}GgG zUo}t_OL0v`Q?QxqG#u`1V+OaBd#B=G>`_xSLpqDuPa=s`6*-jD6;gIK=Meh&Fbo^Ja^h4lQOAvfv-C8(sV#)8K~ zLd!+M9bHx{u>BA_tN9^z*3HSFez4Y(q0V@Hj$&O*2BF;$Zh)KM7I+AG%HnisGlm zH@^!Xcwx?6dtcf2(q1eDXAFGA9K>=Xj?Q=GcVys@uMx_A!L8&2h7XuNK&GhNht{Uw zWI%(k&lzAPU;t|;6(O2r6{v>kBi{f`K(fEwQ)nSC zS|cy1uqt^*;Ec$uz^usPz~aca(YKjzX^)$a$5l~_$;;6)W;u7Ix>8%KJ)v%wcF5aR zHLR{v|AH93<~-wa<0gYKAQl5owwdN-FUGgE0c-;M!69r08-@Y~?=gvQown2*bSL>} zZgd%GU8M4+CIM(>+2JzT@Q>1EAg$!8u*L`>bo87XvT;0ohK@ICaJ0*XQ}`6cGlwGC znH06+6tyNNLq78k`KTT8n0IwoDE*oMtAtJg2|CSPk;xYbR4NO8dniHm$VV8NXxmeA z5xZzumh8XETPBwFj~X){zq{%%iE^*}wdKIBmom5t*M|;n-(e9q55CB17rekZxr$2h9`0rI8-|_69sy4@NT5$Uh9=~<@ zJ@B^3&evcClz#}(&6^)dT(aWlzkl<^uVPL)5o7NAZbdE%P)@l-UW6{>mPi)_7bkaDz0SQ6csKSz;Dg|&k-x=0pd2w=Rh5eqHaaCvc&ac0 zrL_^^v1pVw1x?T<1}7)y%3az;+6Vk!!$;v^-Gm`VS4;zQN=2{$W|dgxoF)%pm8Gin zXe$KI4eG=oV;MuZs_~`hmhLh#rn%38rsY_Rt+f{BtAtcs?rPbDeOXk4A$->231L~3 z+gdawj^Ko)6P&R8tWe9({6qBc5GIKY7zVQx9mtByDHXwRIb~<7ou^4s)uV`r$1>i2F zG&Y!&7IlG3=6<~Qoo z{K^XIRgvl%+qP&>i&v3KgG{!K>~|8ivkF#)pYs*s9bMtpk~|nJT@`&fZKee$-Pq>i zi;rd=n{skpBx$;YLP?53E>lZV#i1cio*+Cq+|F&x0>FghKf4wKOU|MUjb;qi~;ZMR); zcmF$RraGqUjx|riuE=A(unOxaDy%QQSNy_EZQgY;yno#p7e7I)?Epr@wcPJP1fJ_= z<^?5a#F}EwF(d1DX; z2LkS7?d%FT>|iOk&k!PB&9Q<_2^KKuK1kDLdq$3FqiM6bWL4V+0AmheK`}?eEXebd z)Kecx4Pi=PnCB_^%u7=VdTA=3P`!#K-P7L3eFH~nD>m?0H1<5)1**XjCKLK@xXEw`jGXj;4^@FKQqOvz3Fi#HN52i-L?Fbi3g*bd}yfCNB? z_pV@g*`S#wGW1fgTHIXe+EJ}!g$v9(u%IGxDnuGOZrivq5WnTBvlk`Cw45{kwbz($ z-Q9gj+r+u{cjbwTzjF8SrG!tMUYyB%g82mLY}7#KX|bYk!C^`!c(yVj$V(NmieXAN zIIK3Wj1HcqObm7j^OTE~qw>E(`iRaiLz}H7$sM^JEe$G}j96~i z>S`jKa?=JmOShLPVV27$DxfIGIYN3x9`Je>9dK7q=*Ef>)`l>Vh8X|?Nsemrc*NBj z+SwM5Iv)zuQ{EA9tx{L5$2d2g`QxF>e*3lOuX|$2A2j7X$t$Yx^?AE)6F@1+3q&{lDLoNFZJBpdW?9y0z zDin#eMMO)rf(!?Nk>rG+BCA968MO?nRA0Ao>blg7ReJ?*jq`N?+PvW?l7{wT9|1YYjCR>u0^$Wqg450RaPLcRCm& z8HBkqm@T>a+PkFDWS*x>8>CIr7HPNizH~?ufK(-|l-5ccy;J+80ZFcsu)!y=h>>{a zxq;o@H#BxJH$uR3JS+1;h6C(Ib`!gW-OcW2`Q7Xx76CTJ?!!kc>-rhc9JYjNEJZa| zCbz&+p~?C}ll8NISmKqC3EHvK#1j#26f=6KR-j4lx3 zt^`ARjbW6!y|=fQ{o8A=9SgD9WA6Yo@Mv)+982-VhU;8>DOD8ufLco5_W`{;pJHf+ zh3W%Z=^CGC86Qx6K=ahgOc%>#xN&SNw~pf?BF70Vi&!oIpr#-usInHP2xW6pOXo!4 zg`{O{z&cPQg5`jgk>w2vtWr9a=?X(?0-bg;2VRXJbqguf>8Vsw|CLInK@q9vib@kD z76?9DJ?TU_LOeko(@YXD?FOAwiQ^|nn=h1R#AmSx9 z-^VUGwrS7Q3o*OHbkBTF>X#YYt?klFaX{(I^0X+Cl&24G13UvFk{H@K7(_+Z;VX695i5 zx8gHA%W^E=E=|Jdzz>(_$yYO1%I`3L;e{u7Sj%UHjF{)gNS)erZ9Y4npC`f5*PbAK*V0{>^_OhHP2p7=}fpcv=$ifh39oe#{-6wmSl8|Z z(=n$eCGsPwIHLFo+_nLfQxG>P7C+Pv1gN_T#8mBnswXWi$4rWto(fHpL%WZVPA>?#R-xio7IN zRCJK)=xr6GNc#0{DZ1NQ?X_8_wTs=r3;pv0ySG)-0=I2pvU_ivNlUBojvlFWw^i|L zABh)={FMDJ3&mg<|5`BEK{q7pep{4$`0ra2?&YvB-wh!QCbZH*H$%o;n+2?hMJmg? zqSQ^A@~f|D0AIiy5dPrf;&S-Hd&Ng?;&vT>9&RaK)qf$Xx~@ppc)A74&vx3!;^vd3 zXDKlLC9?7anOM5?OC;b4lCJDqPm*gpyErPoXvd27F>dFIwo&fBd8E5{m$2OJWU$U> za8=w!?tPA(jyH!mrixq1t>p$d77vMx7*|Om-$a$A5Vnps0=OF-B5P3&YD}LGYD^Vn z8k38huI(p!hM%9oIxyhpw0gn}Ok;-%Ga}7M$jIwflu+M#7(r$TvC zL~`6uv$VQbxlJ=V3BNZ28Qn;%l(HxY~z}89J5NuD{y`)62n(waN7#rHEdf*#g0#hA)4?Xq1~pbK|-A=<)+CaEl(`J>cOfTfBn55^wcgo zb>%mE=UsUA&12c@{nO6-%Di2hcl6hx@2)s+?ER1TKZv$nbIpuzeZBu}-+KKJ({&hr zNWClpj^P97DYMV~3-i~&A?8ScXNi7LgYouS6Fy|_i|&sOMA?)W)PrFgTd$B0YqF-R z`VbRVFQa_DWWpx9^mLS3tWj#cD%5UOsNJfR>{f;HJEewRL4%r9VWf5|KK{ZBa>`!x z^N3rrs8G{bf%v~NElT)noY=6@Ls7Icx+%IPx;x578PpmI`?weOu`%rHU17?hkMvrW z*Q(=7O;`B@)72_7U0IJG?{@4HP2p(~+PJy&=NhwzsX063jhx?ghgQ3H9vrl1!@MQQ zqAbV^Z)PoCPe4PqJ+vdu!n%p_kZ>eT4rAnEIGdsr=le=Z=;X!c%W!G^qgTGW_>mc= z+}n7`nO8i?W*^)n^+WwEJG|e;x~|@dF>Rbr@ST5Q75*Uv`9|Ug79R zl&6rM{LT^r#poNhAji~6{F!1GKVQ6vUnYue=2&}dcvN(PImMn5o)BHcEt1YL3w9xV zPV`dlQt3kTQv1^Ih0&{FNa8tdAv2qsEiY78FiW^4@(NXsBw4}295Oh>_ZBRR00m3F zH?u34PEbEvf})#HT0BmmaUL&p>W9=R;1#dC?fkt0cM9xw^mL}JS%5$=g_OVurF0S! z9DE;hN?LJ1T>BW-eIzRxYq}SBJPp$TP<7(nrJZE~jpBkNMJnpE_m~%D-hlRT0uKIZ z!W9wl1CxUb4ys_>tBZm$B6tt87pS-=JJ*wQ3GGPcf&MY-egq z3RR^V)Y^)cP)o(>6)RI4QsUUi*u+_pvl8>gh3cZnqQr9X5_Oq*Y2=c`?$qyt??&H^ z|Gx4-@IdAM)Icg+%jV2nXcRlvoXDPKE-*h({#H>m6-#HrNs^|)hm*Pj^wu2_1X3{BaQ7t3p9Hbv1Y<#;PKeduO3%2Dt5sBfQ@n#;H;8#191gSMGj4Fj#H zbufUamli5=WIC5g*1x+@?vWg~NR84A+!XcR#rNoQe z;R7eR=&=^VSmVftbP(T8&Hj!3+2%$buyZ$Eqe+hpa`!I1F8IaY2 z@uVW?d@#vkREg-VUQk zyN0$KEv%?K(XH=!^~+rPzC-_T$I1BNq6cJy6g^l)=QSlx(L))E9?bG-HWhDq;+~^tFwgGY`}1GE z{^l=ud;68V(GR1+XJbsAI>bkx8GGD_qd(xgSb9)~!3x`*c zmab%s^ZuG`zq>+s{Yr>7mXcP2G^9q)n#z1q(rXiVrzmmW2LjC@#(IbB{gfLd!$>_PXwMB_9CN6 zk$5!HJZ0FM5iWt|AY`^cIl4$(BrTE`DT~xa+H!HZv|L`UELWFnz1iM6gA~=K8%C!W z$n%v8vlrH{u3ep8oBoFU9reEY2Z!C?{J8wI`dHoL^*z~NWW)8oF<0XQwLXycfqK^s z=Uqd9S|3RJKn1C$uq*Qm#JY?sv+-0m#3~~y;w1D~6B|a;RAQa6>9O--n`5uVcq3L7 zyFB)OjID~@8$+?5U^EJ045t|kPLNz~lKx*N+y@bWCZwGxdxGIMn%O|w@6(C9O{Mz^&k2A=aL zy{D@&PVc3E#^Atj%bI}KPW6M)-sR(Z_m5?%=9veyVOR z*G;l1%1p|GBpobAR>|$AdAh_4L+Z!LuGCjJ;_CWN-SEmn5`cxULdj+;Mz{-#j!91z3_(uBprN@15K*}grHtFL@yqj~C*N6`g$Lt#bnYj^j5W9qOtku$eoIkTV5;@jTo zM$qFVaVivPL#aTRw5U1c#Oz>OE&$VFAgsbbSixf2!s8EG!$Z6d;WC{pT;_EMXQC0} z9*9%lLxg%AA~sF6C>0$L84)DXaJpxFi+8-(@&{(^91X%G-ehVYyix3u6g6!$EKS~uVP&`bLKtc zdcV_q=B3j|U4icF@A=xuNi%2NdppV>dk14doa9kpERfL!Csjd}H8TKT-pl}nORoHK zWn3K0#75@OEb9m-0$JoCUusm4rtJ`$a^?LBXuE=)qc{j^th`L5wr1s}u$^oZ$p&Fj z)r0rY+snkqD^*svfqJ~5cAiv|X#-)r!AI{nH`b4611Y>Q)CN#5W#v2=C7%f<$z8Au z%@^lMOW{(qOk5^i1FnWwqie-$q^sria6MYb+#%d9-YI<-JS2Tx{vmi&{t4JAY?WUI zzmVSnZ^(ZKf02)Y!}2ivIC2z(<$92n+vVxNktNQt!)+W!<2HZIdD5kUC(15CcZ04k z45+*#V?n!sQP(vd6?ztNoT`x0l6P}>Z18JuZf_1WmD)M9%YrCoBsnNaGGGwOxMl8~ zECboCS?2{=VgTftRH)X7j^jvcB_#F1#CC^U%OMU!j+8?cq2iAwNp;S|L#Ybfk@KP1KmGX_N8vJCscj0$C0{mMsPX7289QH!!wpDqZMF zwIE_;){?!jiYtdLvOMQr^Ub~H>$Hop7vrSZH>f{_jUa4f8)S5rwa~i9Vyu+Aez&(Y zFY5yq5o}Ubb(@*2aKrM>&Z=}9%d1j=Pe?JFV*$%6lA?>Y2?9(|NQw!iLQjK?&?x5g zHZV#UD~{90Gn05nm?}lm3&o41p_Pr|3rQ_2(ScCdrrrT>b3lYd+Ki2ca;t9_V1DkT-# zZcU|IlXpu7UG#FQQm*w_%5>@1ZHJ@s=-5L1F>+c{1Ma-5*!dCq8hP}6kLGK`!o z2Jy#9!w!#=0R#n`6%9+(HF|qC)&a+rauMYsP&uMR;sJIHIjS7bY7OVzAWfwY!VQ|S4D{n((~kLiD3FK9MWw!eHbt(cy& zrES(=zYcL=-&Ua+((~1`rfg{~Z{{Hm?B6P++&Ar^%h8dBcKdc5qs9Djfn5+A)NwW8jqHbGx4IWLuy9@y1c7?AToFeeUUox3IYQ_5thH=(7HY zUfGM59(xC^*?#;rqSYE$pZPyna+oOR>NDCPCpPuLRvtrIHNe^Obq}gYY5wA!R4(Q-7uWT7T7ihj~N#t@cOr1KG9*J8;;R5!Fn; z0|$W&+JQrs5r1+Aj-@=$xIH*{iFV;I43jkBFbvHsb>J{%-av+Iz64&9keMm<;CM-c zTBf`W2XE3g9P)G<+LN^#)f(A2pOdXSas`h}3f>C!q~Qkbva!eA;+;S{Z*+L5)~iP=^0XO-Qju3{ zB6$YC$nm!7yh)pPhw`wdIxjiN(z*rnX<&|~&rof|QR(mz&toH!!!kW@6~7MediR~N#g9=vEPgSm`SjLfMYaDo@T~d80>QcJ;!Nfr z80X^X7t~{iXU)&pE!DyK!A~DBSicRJJt(qu|6R=o)YA2(fYwR5hQz@6>Chw)*Ze6* z>k2t&Fbb2588)x*0mn8{ile4H^o}*<;_t?zd*d;a?5Mkwir7Stk%R^r_bZb5`e2u_ zS!Nu~!8nwvZ*DWmMo=X?tVQiQrB1EWMysQ>QToG{Qg7D>&J54D=LhD8mf6b!%R<-k zS83N;*9ET&-KO1X-EH3;xFh(G{FL&%`JA;Y_=)`2;J>tf^NZj>veFJjb-mO;DjZOf z3Dy{I+-fjJtn?7BQ`RnZh-xT>%y^MAds=;AU-fuEIu-d98?rJK#Knro_qmF znn~1@d?AUFeW-K0fk(s%_MzF1(rG(3I?sN=Ms^=OeTM;Sz=VWM-lAjXq||10y2{K@ z2WVrO(|ei>JVL0mH<4O{MF}3lezK$}#(vUrKWZL65Hk-Hx(~*q=0OT!%sV(p3SxPh z8leTR<;;{VI_AOAK_2{^+ktc7Bf{Y65+9TW2M6BU(VmxU+VeVQ6Wc?1%WF?MpV*;f zxegEi%XIWyVrz`7Y2nB#Vum5hbTp}s?#3AF+-&}K+P=xt*Re`~t<9T)GNk1Dyheu z`E^VsI{XKv5_obaen;=J`_RiGvSEwcXXQ7JgaLSJCC#LK(K!nP4X9pfGV?GmPlglG zL~*h--CP7`quJsDX@bZ3qi5i8O+)wet{-m1HE!at0!-e;`N}tVQQ@IsEMM`ItzFjV4ibDyPe+4;sB# z8{zop&_V!!KmrRiqFtkH(jm|teX+h)KcsWC@k5%trLP9^jSy}Ia5}gg3;+hu?#&=( zn5(M^`y^2xH_6fLC%}Omtr{}>i3_vC{GhX={{vcA=-FbXzQan>e1s@%qvc3F6{r= z<<~BUf4h$n`TMT!Klgg++W>)C%swJL)X*}tXevec$U;!t%HsK1JYa@8!M~2ySrhx_Xi7D93e%~ zBf$U^9Ra$z(0iNqwl>iP~l zhNO}X2~_Z)fYS5R0f?#Jy|#o<-gEc6_~Sk?@KJ~0PNG)?!dkkV`m0oLDSxgtU-k$9 z7V`tYWNI-*F<^?fvJwED3<_c;4={c321myDjp7`PvA_8-{2_?uV(yyi(aw&k2lHOj zO`-~v1{VJ^2V3!1vs1r&tS8T_UmPYQ^b+1&N7s$No~{Foh^vbug~TTH@jXU!aY2eE zf*FM*!mdr!TanF_U|X(atV4D z{)u}PNZ<;01v=7?jlnO0EY;0Opbu8{h|EW9A9~C&1Od;!Gz#LMV6K$s{unj^@E_W3D(QeJ#je*PTTOrPJX70p1cIn^-29IR``S3Hj&lfzHmfX7*_h}E4JzdVjQ<1kL1Ka8J0 zRsoGofreC_*pPi6#2VstumQw|`GauK9fkhGbQo5ValoTNp6EEq6SSy9R)dhMH4Q~E zO^$28&%U!F9*c#;6{QI^546zbpIb>TtJ`NfJlF?C;A`8%Ck~9elsED8SKk**<;G3w zI&-cX(#7!1DIMd+bVlxNxZ#=&w@f-?{_OAFclS4$M~see<+Sz|EVV9!9l1=~u(6Tl zvoD#^-W3Qh8DF^ba*QtnyI=-B1qn}x{sbWODS+s20H8x#IoM>9Xa$}|EV1EJMH~JD zW`5*-hD-eKpWzZmH*)6>eg=a7^qCI^e+LwI!HI*P5&w(Nh~VG5#Inzr|HWra@aZno z{fr5gf(7hCb{Y_Xf#qWrr~^%)9dv?8U^9fyN#qkacMEozAN_PxPr-yYg^O9}NLDP&Tr=4}xs#RZEI`Q-yuN&QR z*>XF4P8Z_Gp4x@~)ttMaGQQxt*W(cg*4B(_Yi+6X_5p7n@%H}v!U@Ole|i7J zoBCN2Uo>Y;VDT&$f;n*^15~+x|E?fo>^X+yA5Hww4xp zG5i&IuegY8|4lCbCV`j-T8N%GvbFeDYwO7O@d11g16}0HuE!hrla{7Y{byq6{^sU3 zl=3br3K;l^eC+ME=C%m>coj(n-1SB-dQt!{>qJCS#Z}8pT@u$voV({8)zc*^b%)GZPyZd%Gz2~J@Qh|`r5=bY6m_~vG2+4*d zge0U80w^jFdb1!Rr+9zF^0$kKInO&ar)NKpvz)!0dYYi+5LYr@9k!H2_8); zW+s{TrhfB%zxig~+xNo5aM11U^?MUnFP~91yL`3fB|N2l#uQ7>hACHU-vE25nQ!Jz z$ywf0kb{@t8>ggMmaNaqUIVoL2IklUXst$|p%A1%u{>;wp{Eb?h;|4j@#Uj0(*-cv z-GgrP2zRsZQOh+X@>tj^oQ=c#N-ck~Y)72sO&EJTjNJ=`1J)K~dBKj6#Cm&s#zw@c zV*>+qv2nn&luZ&7#V9&(BNAylM%%!Xj)LwX4fzA=n&=OMxu|htL#096X9qS+p3>|| zOwKvPwp@qROAF_2xoiFvEBeY7JlvNO9upIumJ}8hc+=F_q~y4)>xo-Z*RP^eo4Urt zZtt2pe(dDV8=r3U2}nu_^z{v}Y+4-@I%!H+FqMG!U`#PECSOo%i?RedmHOBqKaW`8 zzz{Z_8kOdD&4U_(HNgAB8*O0LFZEYG)>WZ})%thx3&9HsL24ubid-nT42$8v+Eb;?+L{rdH5 z=sP~>S7Lnl$;bmZupld8u{)mQju-3j935^@<4X7=Wlay`co^vz>41mbGcsVRYbg`IB2mwsCV~&ZL*=N}ZbCzWmg!Tt zabxM%<5kg9xHOs2DuGjla$la9A5yhvMf-KRo&gziru@_EuqwA_U%h#6OL|P@nmJWH zMNum9$oRgME7DU7rc8;B$W9JA<}qjU{N%)n<%I<;RYkE;lTzb6C~YQN8aNBkW)f&T zE7wiL>(cSs@pxk@-k5^x6S@*eRRYdWz|FCJv7|Zzw|L<$ciiBPE8TIPJKm_r8+Evm z!wsk#m`u#*iV79Q#Hc7mm#?p3N@5r2CS6w>@GJv;wH;?jnwp3c6XVgO&|ophU`#gR zAY+ixBR+U6FxjIX!ES;~E=fF0X-DZjH=r6&u9k^D5Dk2)O;cp3btIe3;9$TobeG; zD^j{Pc^q^v?b$W^%3IneH#Fc~`mA}&^UQaxn4a0Xd!FA7+u}+)@+UVJ$Gd56^UJ8s z9bY*yWW#l#^S8}U9=Bk9S=UWts3fLXR`L`1G~jOmj=0NXtsR?f|#-L>Flrx}ME>Bd_KoVc)nRYsPZF4SpoXIKfEL1-#J> zH@Pi$BZY3bNHa&%qT%vXIG4k7Fb)V%bh*20gZ+GcwSmaTN7toAB-qzStMl^F>3qC6 zL*M}U1@h;FcB3o^T=S=!}3kna5h_*f8XkUSG{Q4b- zu+j4MM~5uOyngAut8LT04{W+`-n~1>TSJHOU*}pZfBd86)z@AUHgA0G<~@&W^(23P z04Ti>FfUf|H0b%^Xm{44vEe!4Ys0yyaJ)VYFAKx#MO^5IBfPM}%gc*2d*Jm(yv&HZ z4S0(lXX=aeBv*qgR1GT9z~OQZPr%p&Bmj&I0BS^rBA+h$Nj{T%a4_sXd_98&g>SH0 zZxi^mqpzk?$V^RTpveHfb_8yPk<-uPNpIl_W$zgb4v=aeE0eh%Uht` zp#7iw9!zcQ{Lj{)2IG((O3GBEyR0XpT z0JGqY!cZc*JFBxH5m&-roH>pxj=^(c@ZtzOCj!@pVRJBE9Ee+d@j5Tu?S=b2aIXhm zY20iiEA*T6q|a@m8(EL>GEkh5;MwKxPr8gzJEBNblsZ}L5`=`V5H(ioytRn;366~o z4+=IZ!h;p|DLLwsfs^bwQft>GYDbzE-sH){q|hg9$I&gh z55Hi66x+nOn-q@pSTOGJl7kjZ zUU~(`TK@0Eoget#7yHtk4_jVdvv=RhRrlYGld4*<+Z%tx-j-)AeU>iE^2ZJfU*IIH z4*$jWozK2@!{(qtIa=7lt>I*=& z@_sCTAfkBQVtrq+L~#V#$ww=*W^T^KD{^sj4mM}usw~`?iD$>+MKQQC2ImLkoFH7} zhhvQxC5YZU7V~j_zECJeg@#U!5<HQ1Iqlc#4ehe3xBV1=Y#I)l|vvE&1TuR3P#@>|S(zq~Fd zW9{3wKk-&v@D*Fz=Wkg!(eP`}nj2o4S~q7$`z*hJBxCIjP3hq?ddhs;@9oUY+xFJ=mKU3Lwa=P1nY^d- z^YhahGK%LUXUxlq9BaOQp8WmgcA#J|ibl6*E$NTK4Ut$C=@Us>!f=%zR{7y7U!3oY zqfE<8{U&ac9RlXkcIsXw85DR;pSF=}uyPO5vy|_PR6n+`YS(S7uG4x>V{0ENTvh(wOmH+vrs%YgsP5VBGIdLu5f5V z4?u6mqL!?jbt$+v1;->zNFbHrI4>OM`{O*nDnF775U#t^@to)Zp33anYW zu3+ZI&YB=~w7GBd%(la8i+&B)t9y3WgpV-Hklgmt>8-^v(r_I!&~M5EmzF68U>?S2 zg>2T~6&gH_#|FJfl>lup5(DU{G=f?OEN!0!X`NxC!6uu`(XWw@3zucpgy$vB-%e*C z=IKKkalwl1Fbg|M6$2+8-!RZg+i&8;@ABSAot*{q@Q;IBHJt~FgU{2qAffmyAFT?u zNt`HZc4&x(*95E7Zd@?^oYv4F{USwD?a=#!#`gzPl0loI1PmPq9hgYA{OM0a-JgH1 z7ejcrNKXBUu`t3ZlF01rEYW%qye%$RXkQ+CoR;fk$*s+^h{jCP8G|`$~ z&3et_8ihvVj>B*&=G+i$)(94nnl zPp_w+YQ;;_d_e%yXf%Gk`l><9#4j`y(>XWcP^?&BIX2|}%roRejD}3)*pN;!FjPzq z4iy8z7g!f4eSj~0Q4)G9YwMaMT$PBu5^?_+oIhsv7?K~0t75QM3|7YAnh5M4fi)4h zARPCF;Fb`a5rQ`djww1?@=T5>2E_$X(HJ-CmHla0ORwZHL7qpPEF_1e1_4UB0InIreEG+ z89H3wG=RyijgQ}YRdrJLBJA^*p}(#$AOFiO*#)Gy|FM?VC)O7iu05Py+WA{7?0pQ2 zzg?VC+kejsod+%NH&}j+$E4t(hi1I9dHegjONw@X@w3&x-&M}uWc>yAA!sLFC>FJ5 z6{W}I$B^`}{4g>hxFDEJ@GbBqu&J&Vngmjwg0Ix{w=_&nI4#!7ENG~p%ujRE~ zN-@ygsPYcvlv=8n(&Y~RY)1`L?MxfqD1?I4P~}5f;nGrl(#EA>`<6}`I``q-e9HAr z<~w_{vwQ9||FVHUXqlF|Xl6oG;gY=kmi+Lf(&qK0rB~eW^2*h(?kL&WccLn}?uPpL zKWm>nsdd+!##_v3bXDJIX~L7Gw+o{1EKi<9D(US5k^ShD6I2TNFy05R(4GzsQON=x zr9aBqc<|ssOOxW4-%VHczbwD=<2aM2>Z}w=h}g6HZW)(kR{e~Kf%ktBxJtDJA7@0)VN2b4;c#{WGD*`n-E%55mR644q zL$9XNqht0#)N~)2?4~rJif3%NdyKbl%=Ezd<|NfYJSKWVxJkk7Q>#;|dh>a9-c(|# z;12?iM4$<%K5J@vdVV@dPsvXq6UG#bAvM}&EomgUlHhZxj? z2FVEwF?cAGf{l7rWH9Dfujt=YwvlAKF$lad^s;@Ohzxh&3vbF86585(9_h*_X?*iN z3-4J~Fs0|NrbRcUDgK9!D@ufPdJ8~?sfsax&|z6dt(!Vh!4oZs zS1Qy>#_5BvP-uQ-5H|7B27Vm9hn?mXj<0>>>8Brg>M3r|W~{ax-%NjDZ7JhEgPmJE zD$W|yABI~DD-5K@fZg3uWNd8U7;S``TL^6E#Xu4m7p&B}MFe{Kxak8G2CCmswSMZL z!_LPhQ<1i_J0Izk#4cT?G>5f{`z&?I11qa;Y@hm&$C2#jq8NV4EiKJkD#8xx$IPBl zyf`Q1px?r~`f?-7uB#2WJ9Ng%nwze?a(dU^IizRkNcFW9u_?9dD~X@&6fy{SFc`Gu zuB_t96wG*{CK}HP#m!#0!Gss-aF2GKmNaN_uX4STC=*u%Zwe+gpwe|ABy{bFz!4E+ z6T3v%)5HcVK}!w^_J9o^nksK$?8lSnvg#RE49c%^=`p0w)m2H4Lc|E?X$I62ukevybPMI0q!5l9i$B^t3Y zKA67FFErRo!I)~81Zi~iwbDM zb$i<*0}kB5=OW{wU>vRn}-){w)(Bs820<-&2A2bai2gLbOo z_@%F0WifU?i=Tb9q(ksD>Xf`ftM?GvaGqs=Upmwci}|?tIRC)-G2@~v@6ywr-7x-m z7{4b9MTzLjtYnmEj0jLf#E1bH#Ta8qKukc4HrXpN5@|&((IT(-jxl)0n0;f&m@#3Y z@w&rY+@oRfS~uIqtad5A*HjDIqB}bku4OLuR9mDQ38z=h(Oj~k=6_EwT2}M9742N>H~dw=01;?;R{o4IoF0PH{cw>U z7pQTK3KywxC5LCj+8*KMg(7xD?2C9Pf=h|OD8d*aMsN`kQGpR5ZWvetMRa)#|Dk|JAzvI*StNg34zSF$^)y?^pyIxz_aBF9Y z@1gvU=4_k`JKHt0*8Go%+pD*k$5-xsZ~dmXZkU-|y>dqA8<|UfR^NSB>)6e!fs^S> zZiJas11D!@g-y`m93`GkFdtzwhz4RX_;^JkwFv9ATfExMD;~kjjc}jHBUEGuo1y8$ z)3X~t02_u-%NLeETe1d@wcI!_KDFVd`lI|(%U>*ivV3a!;I_1;+gsaq&x@thracLV zo@mj`tZ21b%kx=UoT9}@YXlzRv|6lJY9bL9LA_MPYcyC9PbeF?#bY}g9kuZgC1o0r z7FIymhtdy-vwuGPL=V>n+llo~wESQ=Ge=`$#-31CX@*`e(uXz0@cE}nbc^87xF8tRp}=`e6p zC~#9@zaNhD!|Q!#rGdEA3>NQifk5MeNcMxr1wC@W}R z5Fg|n2|Ey#Up$FAU3TloJMgZmyR$9-GT+jeHlb{!4?Qw4KuQi1x1n!=S2FPPQth}5YUhxDmWBg1_#LnT$8HtU`M``3>367b z*}wp!cdYdr@+pMeQB0O@f)cw&8jYTs2(>#DdQ7d+G9iRzVXCx$r+kc=wh%Qg-CI_Q z$fwD3*OcG$;Jw^EnK|K_0o>BGb58899`yJmGOIF~d}kZ?IyUYVS(z#=*1B=fH2^+O zgaG>7IKXp++|(LPq#Nhy#(^&5Cep1or4CYv3ZmdJqtBx@ke@ne${1!YwjH2vyanCg zH_1H%wvfUgKe%4uYn1l}81_t9W!oO`>yBC)A2@uNjQP98n*{8#tOO8$nT)jr%Y07d zU4de=Oj7F#6)%ew$2uX)6D28apGgo*4YzT~FhJ7zr6-c4dK5`ej~BWlYwm7O+~!MzW9_x>X$Ys-ELv}E&cf-7^8i|RBMm=+T)Vi4LfLd z_Y8Z(wqZQ|@q;4i0j1@>&6WrF2QYAlXMmb$gMF~yQEoGU#NHDPKsE2N3&oH{lgJ11 zJXf%JZp%uq1R+$>8)k(VtLamnFym^Kry4eX+DM}YYczUdo82u0FD5GqAu4)nJ4?4l zjT_Y6fT)r9wQt;1TCw}cRn=SS z#*y^;O%;_}8#Bh$Z{^oi-um|1HE-{!sJZobz1u$8Q+9%%)V|N$`1AIOWX;XATQ}%Iw=@{78%vjryX{jPbwTm-~ z;!TXw`}yNQ=>X8{Kg-(FpN7|^;*E)TV+dW6a!1tgzvav&1CR8lRkGF?5(Ef)Wku+$V&!y*2`0ya0UR z1G}s0tTCS6^lf7iM)rta1pWD!7{~<%x`p|wBPFo)^7QnLSE)2&yp}CQ8nzIz6;o2b zY;2NBQ7^+?)|3;uaE2N(0ld6@aHw?qE0llXPj5s{o?4Xh^8pgru&-m%{r9X~JTwna z+_+=qM$1DueSChr$ztS}iiN$^*WKaGr|iKcRSmP)?+uDTTK#~BCZbKU}gWf2Sl)&gj&aS$0Q#Bi}nJ_;uIxug= z{CWF&U>|*;;hLw{9Nt7$+13OlV;UV&gp_ZYV|c`@)w}FFW3$( zC`+Tlk+8<{@k**M5UMZG-R7%9uQ0WZK2s6OePLO7*s_{?ihKP;68HK(TE|Y9r4pzk z1kK4BU*q5GPiA{9@*3O3RO)i+SZ=$tkp znu5;31KD`#G=`TI557bbI;CZA`^p20C-`wsC_Eyk&YiZXZ;I&`_``8s?e#MfQp=j# zTjI&}LmlxIGqRKXQ|4S%Om;x8)gT)Spw|Y_Gbhf*xC(Y4TD_9zh?4M;^v5BDx>72aHVS&3Q71&|b)I_N4SKBC>o)3elMYAguuePRgJXQKzYo^>;F~m<-l-ryej2Th zRuk#xpVL$(H7W z{R4Uby^Jc0C_SDf_%OjY61<4u8iLaajv<&V^6&Lu@6V0*ukt7Uk^X`n`B7~}OR&zi zS09u}XNO=QphQDSXR+TFp__G3=N#xtb2?}E^D_a|7nm9GnNRACHh^Z-?=AOJPo3LNeP_AoW`ryr3WnP|Yq%&ze zkj~wx@z5A($W7y+#VSvwr&@~@)bUVzsv|WXo*IpZ52%fvdI9MW;dreMYe9Y1c|sKV zXtf$o4;|A2)fj1p=}MngVYil~gO9;MBmG!CW+Y+i6a9%ib!f7|z~G!NUX(5pVc6&7 z`GybQACqCw#97`wg5R(#eETcUV68e1yFG8I#z~VZJdIN=t4IY2vK$Ufbk|SC)kB{- zj#ogFvVxV73f_&!LZpZu0>s7gIX{q*;{*me96JkkXHTXKyE3zVtDxT-@ zA9$>XHY2rqPf&wQCU(ElLk&tT54&Z$=g|{mJUu=Kyw~r$AJp+{Y)TkZS*^c=w?Wu6gKY&zMLL^0xzIe{gJ~`@VfcCEjCVJgruG4+J+V z{z#(qJ0YY*@BDlg@}^&6dZYnF!amu#!`7tz6Pz?`t8{ z+r~;x#aBut?R|}dlB-$CEL;KCvJ=oX?S$H{RnifiXy;$Gs7{A~U@ z{vQ5IWq|UqYDkme)}Xyb_qu+p@Uig<<0fW*FIo2q9?hO(z4TtUdi(jr`+Vkmi(jt) zX8*4P9}jvp`2LVK@le>92;Yc95r-oG9({Z4vbe$c4UTq9YvTT7oRXQkGOc9n|BSn5 z{L|@oW)x<6Pa>23rsPbWpVgLaoW3Y0CpSOu`oaf`zARofbNi)ZOU9MFSMuJh33j{W zEbQZU`_Adw(zw!lub4J!NA0NniQ2h8h+Q^nN9{#ucU*Dr6@#O8)Q;LwJ8DPms2#PV zcGQmAQ9Ei+$CkY|Y|F(_dr{e#Q9EicY1=5@v2ZmD&tbLA1vdlg&jHuS z!X_3Df;<_l{tT#@x&`cNF|?Hdb<%Jp%Tvut<>XFIq?$dd{6&~Ok7N3g3o%q?Ik z^p~cYP!A1PGPjz!bFJT?No>@U7?mbLZ8_kYSU3pkoWy!HiS=p{)JCt;kxqizXt`Zxh#DyOFzVB`4G_X zU2qS9OHC$u3en$;(jU(HvlNbTpc{*E?Dw<+*(--RltZqT!@NTt1@a}&$YCYqe@70h zP=h^>8YPo&<*){AQYhuHUf>lwY;V`adXF3BFh&Z`JLE7yD$kGQFo(iCe=mo5hdc_T z^E@Vpl}P1fdt^lcA32W{yBOITO|VPoJt7FM$K zHWpT~^c5_uX5mdNtbvXOlRwL0iH1^GqM;O)XefnwhddGurLaUpDXc;Z1NX>biH1^G zqM;Pl3!Z@|SlG>>|61C&cq0qz9MT0Eju%sWJRuI@2$o->XB-Q+vhWxdX7p4$^k41Jmd+tvXB*2sNJOb98KnRY zh^PWJAv5?hQ9Ejb|8CTWI#|kdh;>4kx^7R)Es;6BZsmNOCfvfdwyfoGbb@xKqPL|RGxf&SdL`b*IMq z(ui!MsAGMi9MHg8q&+~b-v)2UU;LyLeOt*jjp#X$aMHiC9L-C|J{qg9uE zJn2v`<>%p=Wh548SOU4a*)>{fA?t^2K3(UgTP`7SO6N%_jrK7l*yF>zaWZu}SiAws)I&T$<`XK9 zQo9NEc3ejw%~&s^Q$3TMk!aQ{kF=S|R4bDno20wao|b84p%};?>%^-g*Dm$pY-#H# z2{!)hWV~;~)C1kv4`Hb*CJu#Cvs4 zTqQ|%x740p?O zOLZ07D$&TKxs{F5MrCKNPGB=r$HEOVADoP6u96VLke!Y*6ZVEl>`EeQV===mGn>^q zNTU>51UYQilH@w)yP#yOEWN{M(PgKYtv6?~4xJ|Mi-9h66x-?$gY4Yg0%_7b*|^5c z*34FUy%^^1?_0}kTzTsCjLuxCU2?h{Gy`eAB`z?_Z7pKF+$PU^0vmIuyv|72Ly@75 z(O;Tp8y`!2)gdFe)B;@xq;;ju&M9?hcs+N;vmanC>=deFV{ez&92O&&q_-?kIO1Vcyw*(h- zv(etQG^Lt^r;g*c^{dlKvy;X!yb{^wkcOAA6{i{cYKxm4IE3PsG(%lb+wcl2^?D)e zi&WmqzKh(m3<pnzKd~b8>?NCB5CDyT+z>#18nPC0m@}p zXQ4cZ&4$&ojHMJp8r56N;7S$5av(JaQlcS8xqLmE&D3nR4i!MYO18#Hb(Dcy0`VG# zad}8&aT=Qe`AeWKS~eF|vo>;}wsMxUjMY68Qj5XQmGjXO(;=l2;xwGkP*7^G1WHM} zg+h5vlzLhL>7spv&b=yRE!uiE6Jljhe}Q~88|p4(_0j&*_VZY{#NN+5xu@BTMzm&H z=XB^%F^kdEO7Ke|UpZ?(n~hoOZ3!Dg9$c5klFNEPTTYb6D&?c}sgkeIIime520J{e zY(|3u)|+AaO$WaedQR)lhpV(S%~Q-~QOY@um8WB*qb+8!;ZaF5HJy!yQkBvm2f{Pq zKi^KfGUiHsDRZdJnRc_;_2FF7__AepI-}ey7L#UUI*V1XxuRDSSlnE1ZtF5n6uZo3(Y$1#xuL<_Aht@WVuQJ> zzO%W54v@9cVD7GKZtY6U?resJpoKcIyR)vryriykvDn^tYUaHn)i-&Gk*~ zt#w@qVrgA>XLEgXomgJS#@HpMWK2l4w;*=)bab>f!{{2@+qx6Qn)V)XNnM}V1LN$b z6s5^xce_~MX|C%wCx{KrT^&H>1hKBILG0*khO70E#SDI3m)K$MT+-a#4b?5|W7M?K ztQ#%>={s$KM%qFG^%>3W-RbCTZ|JG-P7o=tK-mOZ#?}A~thWh9=FqcVXtcSlzO|=; zGM26P?QN}nVoYg7h(R zJIgX9rIR%4^`&%jx)}GvxPb&_D7C1w4k*)*AU1Y_WCHipH`R46f-zGX1Fc|2p}5$- z5G1mV5~YrziH*-re-yMsbzNQUAUTvj8rti7mcWeENkZS;3Y3YVH9Cz}ESKTrNG$77 zgPG!iG!G-@6MLJxn`p8FyCul%Mtg6&+}aF$Dz#1P>XZN#T3|v%N0=ZkX>Vw5q`sLE zq@xE0)YZfU7OGy@Lxs1CCdym_BTRyUcbNg9p$0nFGL=T`swD8xq$D&lrCBF>o7$Ir z&lsr~^mMjCXUwdEhIW8m)|VD@eYcIlhWQwnzoD54UZ%w2bqm{p zWwWXZb8>UU=e8~@@^W!j znOHcpw74)A(h5ta7gy#KmgI}mpj^o;SbPdW5~0$HSt4yvuBtG%oK`k7w`_U=M6;(A z78h33B#3#16(zLJJg78VEX^*fD4bqdoLwfCR+g2{D$j-HbD-Li!jil)XeD=MZb?NV zv6yD(wefu{DKOxU{-NXE+kINg`Q$Xm|a#_P6?7%HVdk!EWkDj%Up0!{2vv%oz>F9a;rFq^i&EDvl`{)0A$i+{{^v}UIruGwi1%XYDRFrUKD;Pd&(;ATMXIzT?E zr%QO%gdf6paERe2-QRYyr@&|(^7G>eS)vzp|{Og!Jw&DOLkdtS#mPulIlV9y~CuRY(oS_h{zU%$yRa;;>aGd2g0|K zTOquc?1k{p$ZZh5o%|KTeLFz3#>BaZXn@*teg&46$*cQu4pbE^^IuHlYB z_@CSf2oG@<2wOQT;&{Y&AI#0;#3Dzk3sm5IvWx7H1#UP zsjpJQ=+vv#dm;QY^*0dyKlL#P|5F34XlgXQh|?_BY7o}CY4wQH3fdS5$7<6cJXU)b zgzwfq2;qmcPeb?_?I47IqkRd&FKb^xMEk1tO9=l(dlb^Y(f$L%$F$!<_+Q$8LHM{9 zD6jpu_B#mw2PTixVcoL`>jrhtL-@D4zeD&Rx?_mw{;4-2tT*ZX5U2OoS3!8TejbG9 z3(&6cjPML1!m|cngbjX%P(%!2hA6}tq773ZWvXE+gtH8jDr{U(@G(~Txb zH{E2~g^1~9(=)(>yv&INg))ASI6>k8dG>+wWkBZ&HO!{ES`Bno&r!oz)OG55a2wSf z;4V}5fx80ch|bQn;9jS`4pP>r*Fbo!x*x*p)f*waNxd1uJ75Os-2GjiDFSm858(uD z5>Pl}dk*OQJW!j`)*D#SRd#QMI@R;vdcircEX$!d zPTw-jAqB{{zZ_N~qx{8g`VCJL{aQB*tC2ejlEWJ0fr1%LX`E+0@kAjGDHv&y7a*Q3 z1tV{$mlRhNh$y(CY=#I}E#)MD4n8PUj&Y!^_@Xd5#$4s-v++;5nB zoVu7SHg~pR9djd?JBhg!%xz`vTIOzI?hfYeV(wmggAf0lxeqb-DdxV++_#zgIdi|J zE84>9*C=03-8*d3_pc?KajpsY90 z%ok|m4|5d=^Ah|cq}zVuGqS4ypFl5p2z`HyXKTwyS1@oE{l$+Od^g}A9dLsHyorFf zLV%k>fir*`P$Y^1oiqk^qH!o5IB5*5q)F^)`&5*M#-ed(JW59sPzLM|CZb7b@+oRP z-84dfopf5h5hUin`D?Ko_Q!EJ6Ib8{+>O`b8}WX85D(%%;6LGGgeRUPf{Z0Oq=Gb% z1LOnFn=9e^xu>|Vd4IluU&i-=>Ex@ujMZkO(W?hxS1o4U_+-|CfmZ+(=0ygpA~rEk7UcTssCL6t)LXVg(zXXkSA0LjlwdaU)Uua5Dp2?32zFY3*Q=)25&=@VZ0&F zP-SQ|EHm^Qb{P&B4jG;^ylME{@GW4qw=v2%-k4{sGBz5Q8T*a9j0cQ|jL#Y0G=6UU z7SUfvVfIfDG8{wn7g$mpaO1<@ImiDskHrbhjl1-{fbRxm|8s|&z(@a?;t+>_;uu## zyv#YS)-k@bwS+Z4caA52*Pk#0z#i$BVW&$7j5VG{hlZ{W8*EG~A!&Or+r) z;;nBxln2}Pt#iEN8t3@Zy)3Q-I{Aad#DP>~N>_X5%KkFh-y!?=%Ko72f52)`0KfVo zdQy}ur~g^NW0?+cWDc|3imXs@qP z_See(&t?DDvVV&4^?fb*E8Jv1M)vb$zk!vb>ykHMVw9ZPE&Dgh{z2LQ!-%;*m;9AV z*_YE-=8cqlWwq=tm;K$ce|W^)FUtO3B!87Wj#Y6^vV>PnbBTZCEHT%8$Ko7dpeK@P zz9vrg=g9sJ=Q`Fr#o{hKS~o@ZD`bD4?0>^@`vOu=fjvM%bM!Lsz4Iv<-G8 zx1j^*0dxpGg`PvNqBqe8=yUXqRJvd8XTLme{Ux$5&vU;#&+9#9U+(+*4YDuu@rEMV zzfJa!N&ZGTeWP68#_uG5lRSz|cgw!amz(AE&G$+E7Fj;F%#(dtzP21^qYgtwu>YIG z&c8a*3UnRXgsw+>(9hAm=n*u4oyzwFCB-XTl;&hfG@^V!b7O8)gNvM-P9h6vf0 zDRyIl?8}t9Nv7B?Io~dsQa2~czFcm%QTAmC-7SyfmKCypT=MsH$iB?cx5|=z>j#p* zSC)^xe{kl;y`MO9^nn#Du49~;I0Dy6xemy_ETebHczM?!WM9UIyJh^jdz0+TqrO|F z+C5paFZb@A11HPVJ@Sa|&5`|OERPnhM}ST8E5a4`+2`SuKS;q{0C&de&8L+e^4ArW*?M${h%zX56M#Y&@(LeM6?R6MO)B~ zXfN82?n4LBlV}jVjQ)V$Lw`bFqhsg<=CKZY;y@gM6Yy9(3FqKq(01m6zS9aC&nnP) zwt&{N7xbR{K=XMLbf1?&`*{!apRe&Td;)YJ9cV#;pa&&@CNv3jp<=lYa?c;WTJj%B zko-qwG<{TFF%HTjeoV&8Loy{Em(lC6j7v|*d^#W_<-p%1{}*ktFZ1=2GH3i!rp;42 z*_V;#S91NoPL+Kbsh*bQ<{4Rzo|Td4S$PE<6l7oKq~FNn_{{^7|6G;iKQFKE&&x>i zTRHu=vfR8N_wU70*_RRPB{}_Nx!`Pvlac%B4P)OMTkmOovZ*JNNn1XGW^+Px35(Hcj?r z-1?k%o`ugHsSft@wf`l4owEk|`6ie6HkbH~F7Z7s@qI4w11|CVT;dP7#2<2rKjIQU z=n_BV5+87h|Jo(~oJ;&=m-z2p;(u_7zvB}B$R+-{OZ;n>_%|-`V=nRUoa0|0m$=d; z?&cCVy2QO*;(;#lFqe3QOFYUY9^(>^bBQOo#1mcO$u99!m-twh_;{E21ebWGOFYXZ zp63!Tc9xef%ADh0Iqzlh(cRARZ&o?S|4Llq9nSH86gkI_ed8SerwfYy3%SJGoa4un zo#X$0$2tC=tDWORG0x~>RlCF+T;jiWDo;?LbDT6G@~ki2)&FMfi_1aNzYUF=^JuKw z;h()NNPcI~+1Ik2*>`C@=Tg_Gxp<9r!g;8p35}XFGE}-wN^1gpaAaCi8FZGwOO@u1 zqqIJB4)p}tL)+11Z>+WO-))bbwai)%j*!+Q+0!d?sZ!fkCt%;DW1wTS z4%pKN&avJcM!SQ~t1IK}VO{y)Fcr}yYiw()^=*5~_2*dQc1imO>p&Mqtzj;-?+>is zpi9;O<-T!Q>s~$Uy5zOOn!_l#@+7{?VeK_h|I(FLYODj+L3un)*1ZmEMT>Rs57y>? zL(@p~lQ!qXO~vRZZASO9XK#KAJF!u7{>=G03m7$$vA%wCXw*0wYm4LQiB)M!`!A)o zqVsGn#a-|DqTQM7uSTZR=)H)`-&}}&#gA>Q3r?M8ebV|Q8Z{T($mhmvRz`J{volnZ zN6nd;bH88l(=|VhI}7K>{9tG9A?SRY{ni|6Pj%UP_XvsA=z!%XkMxx$un(I|t z{B&tPYA&Q1-pjJs02(!?WzOgQ*`K2M?`L&CuCc!1x>CvMY}Yfq=W%)N@!gS9FN}c^ z&^?+>JGdxzX&1kKLd}KL@2p$F=E-5|-|Mi`X1_(CcctX(P4crYu5-K4R>O~Ppm*#{ zox0H9Qgl%mddKNfx$AUh_sP!vuC~luKS8`ZUrz)`crilyMrYbxyyn8K71pP%+pS+% zzhL1nM!5ckW5_u+Qu>c?tWTeDi65HpnnwBV%#HP3=*?w)1|>iD`5ij%=KFu6aj}_; zL>Kr4wP6GE?F z2;=nIh<8u#%-trlRHHwaC$Uf3S3EiLzJi8Nxv%K^>Fz76k60g({E@CX#r`;Qj*&|K zIL5l+EXtfGjec}Ldv3}KW9Gj*(zR2TdENS&EeFJ2LqADleL=dr1xYlhcS8j{voTiW^2FwXz)O2OnXL>|L>`z8D@upVIjUm*9|=f71z%Q@sa z3qFB1kJ#6R9O-$-^S4U;P)5d>FKsSRZ@=|*M#KHqC&53(2%OW;l{26%hx(-FQ!WA8 zJqc7gLa8K6%afpsKMD2#jAyHpEV+zjI4+m|(k^3XJZkGrPPDVv1HCxn)Y3(@uaIg7 z>TLyX+j{bv26WjfVTHXP@_9M4y>iUH{!2N}q92#bzQTvi_Q=mwJO6sfhH~e3EtG5h zo?cu;=3IFChr*>xW&bX(6%w}i*ttmZ?JMZXr}Q?uoiqI|Y5mT&cVsIpTU{Mj;2*s8 z(_AYqM&qcXAj$tVW_Od%hKe_M(3M#qbt zc1$yN!4wwX?|Q1?yzZSmrgIsk^?e8ZcR%aZ7mRZIhtF52v0NqfF-iaNsUp)&sIl1kTX2SAUxlgA_3);_9)phO$BxJu$&YaKCm@jc9mFWFI zCwfY-m7TBbr{!c<-n}}E{%rQMKlu6G`8T#6I`n<3JXewgpF-0+zx#8?9SfZqbO{+7 z(%EQYpYi{qBir|6=gb)=&&8#6Kl&8C{qgnsOX?iud(G+Sx|hHi)Q^9U@y9uL{88@D zp7(p(wi9>ItN279V3D=sJBJ`SZ$sWY9${fg7v zlU-bf-PMuxV)^_|(t!7$M9uxqvGb{KUYfK!Q!^69*!@|AF7v0{&c@h&%}+Vaw&z9m za~7FfkFXuFv=jbvl!i8AyT8pSXTR4??|-A=Q~D#agh`t94|e8uruSzrv~lJ%=bgmJ zXmshF^Sh zcPiujB&*|@q$}-4b>k6@&-ca8_cIkIn=`#Xd!db;)2Ie)f9iAi3A2;#fB%3+I+r~o zb3u{V_WVDi-SF9`B&S~tpA-z|AC=i*Gt%kY$!mqfslh1iWaF^5p7czY!=B>HQT^`w z%&BOHM$LaU-}B7fsBV0g#{NaK(YGX>!T_52xSku!E3nHG@uO1gYFvKVb;L2u+#g|2 zb{6{GsF98Q9nsT&c5#9HExu6=;5?b{`+Cez>zgyD|2>h>FOLix>nF@T?Qc9<2c;4} zHDldwZ92Dauw1I&fwcBJeCNIY9O?=BamrjA#`#UeXY+ zR4vDUoH7@Ov95B8U47Q|NbkxGI&dizyP^AvbFASq`M$jETSwCF{Pah4Hs4G60lsg30nMd}+!u$r zxV}|>shKnXwwq+m<{Mi-#CN$aoH={c9>qJC??jz}k-igkW`>rzaNpNC-^TX69v1$| zd|~bU7}qbrp8i@fe5!B;t(`@gi`SgZJ(sipw%g??v+_5#PPf$HIktL^kxKtU-;knKLim>6huYTL_^$&j_8Sj_z*u5 zKmth!5lJYCAWOd^xX6f%`$k!&)JOeZ-cm*kOr zQa}nx5t%`X$xKp0W|2~I1t}xtq=HnEDl(f?lNvIITuJ7VS~8E!Cksd&X&`3ONSa6s zSxj2V64FN6NeAg7%SbO-PWs6PvPIvcU#8!v-=@D&zgxdYzgK^o{&xKx`aAXa>i<`N zpZyPUHp+Bbor~X_0klv!V3P`{L5ja65 zxCuHzFPH=m!Bg-R{DnXvNC*~0AyfzxqJ(H6Mu-#Qg#=-YkSHVxDMFesRv0IY7t)0c zAyb$rOcEvwQ-rBPmM~q&7YcsFt!fc^NSR^zF%|eT?SZEcN z2yH^U&><`pI)yHwTj&v%3BAH{p-)&LtQ1xu0`tp(#lRnWhV@ zNC`ZXhE%{eK3~_&5#1yYMck^9lR})b=cX7HW7NKaawIw_iu$z~4uZH}LqIkmr5;KBRnz zKLqz<{4uzn;!ok~XZSM|0lfb=6bbzQPZR|*@GXi4IrtZf0a^GripBrI|3Pse69~n_ zP%ugW*-)S{ARj7}2r{BZNgyX0lnk=shEhOYbSM>MMvr1ZZVV_5WXA`M1^Mwq<3NT2 z(0GueK$H%$6oMvzJc)?PR4B>-xr#uUAX`z85=&yijVJNoCXfU)5o9e9O#*pKLX$z} zQqUBTyRm30$lf@V1@f1UvOxwXplKk78E`d|WTNRHj}uW2$mArH3vxLb<$-KYLHQt` zQ&9oPXcj62In72zR92x>4#`0?Kw@)IF-UD5nhBDd59tM@0F{6Q7ecs*6roul$um$X zNOLi|0wj7SDg&u5LFFLXvrq*{cPXl*5)S#xNja(lIj=yoLDnlFrHWLcYLNNas0QS| z8qEROuYvSAWDdF#FyKlw7jR%Mss${lMe_g;=Arq33G>kcz=Z{<4zQsPEd+dMK=ptT zX4C*U(TL1|6-}rS@S+7R0?b&9ngBOiQ8Qr264V0t(S{ZShP0zrz>yBL1hAwFwE>GDPuaBkBdr*@l(_?%as_0DE?$ z6@Wi`Pyk@iUbGT$=r*(pu;_Ml72wew=xV^EJJD*ur3azxv-)QtWl;YdgaMVHhEMdL zplbk|K7~AA>5oD^|ImL6^$ZCJT?N?0p{oI#RHzs5$qii#7^Oqk0Z!>5Y!Xap4d9gr zS__!v2`RpUFQoVj{%9TGS0L&K3=2Z*0mp*T2EZ~AZ3H|EMecxUVURych=TmlLNwY0 z_!a}{aY7t~gSG(XC8Di>dr4>;U|$N_PVo=z00bO|b^;2HN7n-q zrlT7G4KvVU4T%9gVhj6}-k9GrU z7NT1KIg6mJ8Nv*-2N1Lv>YOReM7IKxmY}_Wrn8`)GNBCIa-kghP$^V`3+M_p)Ce`` zXMnLwQ4e71DzprQ_|GziD!>vzMK^FMo^pVD20&6HSOwsi3BnX%l_&_T3I&5zGqly9 zRIqLgTeS>Zbqrhe3|j@11J;1@z#35jSQA5AcT^141C@a_p>nXEr~<4PLt$@J1=a^u zgY{*I?8gw;~2umGYplmGMu4g0zQt9qcIE_6B#ll zF-%Nmm>9`$Foofugn(%b0ml*}F#`6PhzZ3Kcj68R=s`RH0X>N)AfPw#2JE9KnMr(! zFCZPo%1OkZ_yf`nBjywmM1lbCf=Mvo9YxPB$7k|-ccmY zBhe%p@GgeLKwA`53rHM^1Jt9~nn%WvF@SyJ$#`%nV$NXrSj>=dCPT&&hK92k8kRB~ zyn^9i8AHHwhJY0e0V^2-Rxt#e4cK=DU>rrg8isTd#$CxUZZ5;PT844+7{<+K7`K37 zTph!>g$(2B$wIOa(5;@-1G-VXYh-x0h~ZrmSwt2Ax>3|?A@ zt6!yG1qgVR{wlz)YxUQHyGFkTaFF8TO8sX2W| zuVyH@nxW)13?;AC|BwEE&~^I#`u&jSfc^kl!w_?&{vrKCkTRfu3S5ew*D~~6sefMo zBBcCY{|2}p=s$o~{;dBCg#V%cC)EEfVCY(gq1P}By_R922hXU={km_ z>ll*uGYnnFFtnec=X!>o8yI?SWaznxq333Xp6i52Arf$nVrW0Z(5(zZHw&>sETmH; z-O4a@8$-|S3_W)U$wD$9CdJU}g;XIG&@D|!1H>E)c)F9}={AO>HwhDj36MhZbQj?1 zBnVRsy^&$)PKKd3GYs7=WDD7lLXmW*kR#+ko?Iah!W31v3k86xI~l5OXQ;Z3q3TTx zRc~Rax`(0aPN7sN1)RMCuyrTH*1bXnVCzo6)+%UaHX!Uyp<1X0grzvUli}<#hO@oG z{lfjIPk2Ij60HEN)u9crr7)65sMbpUMvf!IxpVGF&E@|;d+!1t)m80}?|tUXnfH4# znM@{cOj8;WDI%pvBSuUqB1TFnO;bdQ5hGF>F=8&Ih=?)9NNL1ehDN>4vDMq9*7m+6awZ7-fWD-Ij_qO(TKiALstaa8|d+oK?UVH7wIkV3h z(_O-5zSTTgxXn||w+pZKUhOUs)aGg*5~;0@)=zZM#xVYE1(f1gQrHTkC ze^xG0*%_v6p_pzmO%a;uQB%JNne)u~qS`!NyIp)mo1wi&enl(MJ}MvAKBj$4epCCn zRxTU0RocU{$?@-w{c{jzt9@?GD9zV*rvd|P~5l&3|R5YCGPmxXkaC!B*qxKtrsb~<|rLdKaPnm}h0 z&8o=~&%Cj=zx|FV08|NxIuP4|>1 z=)99)w*h+r(zV~`h;kqhl5*{PX$miC^0Irsa;|r70@OOU02-V-oI442JNE+iI}ZX5 zJ3F1n3A&v$)vh`~J;^d`BbuR+Xo6vTUCn^u>lOyr zKIaa=0RVD9jlZH9+UX9N;iv&!&VzuH2Ap=C1N1t3T)nPK@#_J)9-!-ML=(_mw3Q)# z?WSvnAgxDV4l-#3&8LxYs#Q15AAh|BLF`5g)Q zjcdxg6Hx2f&EVPV+yU6{Y5^Q1n&B|f1jDi7=_ELwaNW(|=^@#kG3C!PcrJMQ7+me1 zegm#DcrJJagI9AMVQ{s3oeW+-T{DEe>40(G@qmfWgMcYIxVpU44VY;_kpZ~&&Lug9 z`8vd6)Vs*Lm|-}Ey~_Zx^m6YqhT$0Yu40H?dn?^mhFGk6*Sd4P8{Ct;)y^JoT|#UU zc(T3qo;+`T?Ap7{Th9=`roQQIq<9>bZzlPcc=|p{-{-FI9&oP%v>R~5dlb;+J;@Lo zcVhAGJ?-8KI7c)?FVPH_h$e7v_6`8Hd#^F@80Axj$0?tcuH6JdqJun+`69Gvk2o** zG6{0&I-jodiJn9-H74(w?CfEP^*7%PcLhWIx{$65$GY~-2JG=R1CIIT0ZzHf8GH-e zCjccnP}&l}DNh4~uZ;Kv!{eB51%t1`x5l^5wSi{-8sBDut-hLAUygxqy8%rG>@r}F z0j&nK>A+)v?~t#JHzCdz zGYpT7{%i(+o;%mIkH|f~Y^p_GWbsb@d^#%WO_Z9z2 zj~Y8W*ZWrkYW-CVG#4xbG}4?wFg&06*E6`PotpslL=*To`D+>6r~F$ONLK@(*1v(0kQcc?hA^`a@QsQVRx>-(>=+5 z+}Y!&`P1J+*Jp?O7WXy2_Fov;*F4$&K2M%6H=!T3P(Nz+_mi)#Iy+n^OmUw8Om{5?%p{tjh-iY* z^G<-qqd;t2nmb(oz{9=-4SVsC7%AGp^t6VLBN|!URHn1U3 zP3OA62H$ySf1uta0^0(O47hHl>lV6h;p@OY=heUgXKkRJA#lVs5pdLiF25Q$>GuXs z)A^j=n_$;*hPYj{9$<)H(^vx-aBmG7r5I1CGHLe&(h!$&vH-5wJ2D~5W9At3KlZBx`Jg4u`wsO zB3Qw|>jdgY%NSyLUG2d&fNQ~Z3~nX3kzw@OGFTJb4A|;C$S}NS3T}7L2sXJl2X`?z zyMucef~~G)fVN;Epw+nv(B|9%IOM7Wbhs7+j*(u56Xe@dE)hIKc?d?&ZCL9N#MWZ5 zu{ySv3Z8eIpnJa|b5Ka>R{u`)C6Pn;s=0FQL%dB8znVc-eCBr|kY+{HBizDBT*v6c@UW^6_(EEMJ) zkf{gfA~-?NXGzarsAM^`1DgM?W}_~ZWTy(5tEkJra$f8mWIb z?L^Tk8Vx8yO0vFuj=$QlM;fIRar4XKdNLtRu(HNS;5tBuStKMk3` zp?2Q{{UG>TL7yi42kje_`T)x)5%Wx<8^t>ya}1onqO3=`)4d+8TtnIi*fZ*4#+<6utM3z1oi^0WHBz~(CYB`s{0mY)M|7D=_sXWCp+`ag7`|f7 zXHZr|benc8@Wxj9ca@Y&?@LEM94No{$!4Up$=%P zL23a~%}7mxpIg!TjJG4Ti81wy6Nr$hpc|2z4SFTzJ#V=mwdU6U5^E-WGjo8`pyw&p zAbw-95+2Zw5@#vm>P66>Lf$@DKHmH#?tOzl1*ZO@Lel7au`w<~D&M3zR{{)7L{td89mW{qMZ|i+ zIf+~=VGn!tlBEQgZF?tddvr+S2;_bz|EO^bJ~}uAT5lun`@QTXnQM#y3y8LJJj+=_ z&obu7A$$?>a})5F8EZGArZ0fQt?cLdf})GZv2Gs6s4sjQk@hUS_X$Ye44FQ7wE$71 z$95)LW%@qh?+w!Eqe?D-JJx>?|F0)Hy#Z-ag-Qs0iq-+}g} z2$VlUD?By$7ua?;Qa^|o`yuN7L5?o1mpF4dqG(JjW82V_oDQ_Ai7R7t(w5!3Hle1&p>^tf5|C4 zp38s+8SN2_s2La~c$PndQS{6Z&pZ8t_h4?S;4xuvkR;El@E*^wN)h_YXEBbQ8T@yi z-8eR=KTbtF?1DFbjP=7=&|gKaDX{HH*tQ3?OT(PPqpnk|7mC=V%p@FmgPzX!ikWyW ze?Okfm*Tnn19&dabBT1=#0pvfwG(hJKT78=0v=aR)A<}huW@F$lt2#6W7U(E=RlqNSR41iT~Rx{q&bx?<)_PIl`6s0(_c)N%|+%r zwMp8otVNrq-LB2h?ucn~zvLeD+~mni)ap6pdD3&l^OWylU&XKu{5zfKP?o>Rzth=G zkC+~zvj1wjCZtxWeO}lcM;*Tu$<7y@{UXJE$bCqpdQv=55%s*_xhOKdX0KUf`yThz zQ`}JxHPsPN>zKCDxsia@R;Cs@?;|*1oEh2^XzHyd8hK1K@|aE%(5P;r5ynI#kLi*D z1IGE94l0d0s?`9dsduZvgln4LD6;i4jX)}mL@JFy>LkRjP^tY@8hPlBK&bN!SOAb} ziNRlhy0AT?f$f8hv@Q`U)mx=eRHgP*=?*}pQ3+*73>#wfMuVolsnQ6fZoPrr$m>`h zq0(rjHjR-M%eQMxej>a@%%OH(N@IQ{%{?{Lk9LS%)LRdV4$&og#CaMmu1Zz9Wmsm* zd^trH$whLhTp_FEMp-8tWRq->`(>x>5hmr3(xDtvPAI38Gc*mHR8A}BlwP9FD;J4( znW)1`r_!f%6V*lO=akb#?NM5lGs=0Q4pMrza+auVN~3aAX(4JCr5~a(S5Y=8wUoPo zsCr62qBIg!p{!AwX!KN+^_1SA93<+1vP`K{RuR=o=^JSdP?Wt&u~Ma!64jzCR#qv? zpm~N;Mx&;p>{R9`rAjeTjmmgszA}ZV?aEYTHqA$h;w8NW$_}E^soW{bcnYGe%655D zX_DuZUGfsmd@9K|!PZu#jr|}Lc~YKMtTbCH^1Qq#2jyk*P_x{rw8*{Wv4bdgH(9oy zsC{I0vpi1JW@W2vp*cw5H1cj6Y^HR{7BrFmJv7s(N=S(+rLsiXFUzPkDu@GJ@L{Jc zkxPivpzNUQ-Ne~1XUlowpU;3^;Tez`;kCQER`5MrpVp08o$ryZnod9`-ydeOou&=Y za9*pU5)X+K=78VICCb@?@6&eR?(sa|<;wn{afIhHCO*&7`xwg>+)K=6Od1M+NvCO< zrJQgva3)KdW>}(Z57WviaEkeEjmqNEFEDNXDwjod;1T6Py%vmZ6HFI`w3PE|)e=={ zy>71L)xUaH+sJxwCs1k@xRYE?{5dM!?JF(uJ_X^YkmJ9nBdkv(?!HpD(RISqaubEvI(-i;*8)qIzz zDF5Z6^|>&)4|&bLdd!)8_p)5!XA}3(oOxO4SN^K}mvV(>&i9#$Om~`Qo9^P7ljhCa z%(v6LIZJz=R;1mj&DQRs`SSzXJne(phj~3JkNX1dH&$J z;Q6Dcm*&{d(yaOg-zMJ|ebv4i-v|oSu1KI`uqI)xUo&d`uCL*B*lL{JH&`>% z42g9Zhf|phiQ&I zF1uxqJS#8AKG`p?Dnii|C-34^!b-ZFrHmuacx56TQkRU$ z=Z;sJDXyC%ZZta>v(M1Q;dx>7j6g%%eBe6ZZeSjHdbCwMvRRgJ?JUQcZ}~h@dqFn? zXK{&m4yf`NPWSouLcSdKpCMe2=Ndb74nHXnlQ3Th9kV}4@?Oxhfc3d@Kd>8|9>{M8 zh90(rQoDiYL#6{Xd!Pe0cUaCbwypu*z%!AxgnU)QPm9EJTnnlro_}c#@C>(+kadie ziJ;L6vLBpW(51jAQNlub2Bj}0T&dg%-zvZs3t>?7otmpTh^WMjO{Ddm_vN;lE`R!`{ zUw~SIEd&h&I|%qo3A+jQ8hnQR1P6`u!^SxVoujT9j*B3zsn?1PqFU6^{I^Xsie}M5 z>zxClT^tcd>EO3@x&(R&)?)hl>q%qHa@{Hke5`uLTHP4yn(uKc&-3Cc&*Vh&%0&#$ z0GWc=|0h`R?yv! zP}ksoXS4d4P}IlO$LaQKrgfn(TNhbNMb!FX>qkVEb(wXg7-wB=trBmuK4Sfnm}0H7 zZWHgZ{@Hq2%yZoAm?-8udL0)S)%7)|PTXdG)(pO7Z?sQPPZQ!l75NS``jHpwLO zIP+U%j#jO;$+u|j+7oi2_N4ZtT%>hqPs{tX-&i#Hpv7iMmXBM~Ea|ezl4W^B?zBB( z+o+grU$kvjT(+;-8kA()H*C9=RNMD$oysk?U)xS8@3TE?8&E!A`@21;e9U2U*p$yX z+zyYj!Qpo#D;piZb6ikroGYE%m3rr2U5fHESE?&j`L!#}m7|<+jdzV#e(QRx>#a(U z=aZgKD$j~?A^aBz`Uv_7t_meUE(~Y{P6Ap{1;WO4I>9)C@y7K;1Ev@<(+OtAK?aIQ z*Ia`61dDJMfyH#Dd#gZs0??gUppsy%0nFP#P;KOAs57A6kl#i?_jiG2f|j`auuc02 z4iFR(v_l8mtmo0o6@eoJMNh&|~lhh_=Eu z-9El{-w^clAfJ`f{xbgxe}#XIf1Q7$zsA4Wztz9p-{jxr-{Wufw-J4aQagxq%zuLL zDgPP&dH+TKW&aibAUJISHDC{T{Z|5^K-7OCkWDfqAIPJ;t$_)F$wW_MZGi&9>jD#~ ztk%FRlAJ@5djiFQg@Mw*(!lb-N|Ia+PE}xiV3U7ipf<22&=A-``Wpf}1G@?DB@O!n z2Lp!#oq^+l?m!Rm&jv08`bcJfpg(XmD1sVcXVA}_U^ti_92XoPoEV%EoF1G>oFc*v zfrG)hlxt^jesEE6ac~*sDi5v-RtDDwHw3GRKbIuyg7v{|!A8p4O!Qo$>w+!8eZd34 z_TZ7=(O_3#A>osOh3vQB>EO9wufHvLDLCLi6}*0vYL}Er{9LNnbvP+MX;QE{X=>7pK#%`SQeo0;!t?wWlNKbE1okH_Nh%{fH^4!E z(u$-C#*ic&*bCf8c{}}0No$hU`8$#}2J*;)nxxH1Ta!wXw)?LnH6`sz+LP28SeVq7 zbckwtEvbXrbY0T1q!US}lFlTZPr68wmy@m}4F)EJ)Q~-}Fyv(q=($3nU~ed@b3)mn zywHTuWWv)z1)*7l=Y)zw3qz%>a|B)*TJAqZcqK5?t3y@(i-A7>iqLv$+2f&2q1w=v z!0J##Xh(2cXlJlDX*<;@H?*7M&A*H571|p-7up}F(k=9F4;`d9Wd7k$XXtn!nzSI) z9qJ)_&Qc_o`CCI5f(HV-Lw%wC(A8v-tR*`G4at7cVbGetH90+aE_qyFHMd=8ee(F^ ziOExvrw3b-XC@aVb@+EB&!xVw5q+mEd4BSufSqa=4mAYFg_b4_`cIL3E|t5D>Ey=bW};h?_fZ@5B_E(R zJ(o0?ERyn*+mnxww~0~-?&Mzo=HyGs1IgFIO4v%RurYZc z><$OR5!M;b3{za`bkHBprx>HW;Ys1C;TfUb23^Rsza~7}zbia1ydYc>UJ@<~OyGD( zJ{exYH40adhOJ~(Gtq0p>*Dmra1D(N9Os>(rA$-o5gp#lba*T1?Np;^xG8BxcvqNe zp|?wTm%k(28g2_83g;5P1J)i3p9t&^p9;59Z|V!5A^LpyVrWA6a`=k>VyGZI=)V}g zoK(nkiVB)~M~XevkhCk%pW;mkr9@M*Q}R+Kq)bkkmQoOIO_>$wOPP~WoU)KclKnh_ zCC`LpDVLJ6G&ntFIayLeV^IN(8};E`6#s*PyzmNc3yM|HDJugFDXaZWDYH_lQr4$z z3aK3J$)}UtDYXHW;~{w3qt;l*9g0DV-!!k$gI3WycIdYBa<;R#-ld2>OE{n&{RBLj2 z%2|q>W2tVMn|6g7QiB0+syj8}-<6t~noF(UothttrcO$o>ff3=Bejscv^_OH`J}%i zb#@?{Ixlqr&4z1Ic{HY3Beld|mbwI-vhXC5T#;Ilx+ZlUNxD-vrq-lxPTiWiokyzF zrqo@jd;AwuTd6HphnrH{c!mnkBg-2|>QHJ&>ao=A{uANWWRZFza5k0Z&D1kA#$HK1 zAIM9+n0h&|pXzdjX5=-gZ4?!&Bhym{X_ll}mFA#T!EI5M=+#ksa$QPs@<3o`)Ju_^ zP4nh)|A}ab>1Z^yHFb8fh-L?hqj}K@(aF(iNlPNmXhEvmzd1UK^t43h(5SUJT1+Db zkA!}Pt&MJpHUySNcSLtacSrX|_eT%Hj-RxmP-)uSFpnE0X+_CJY4g(-1^sD@sV9chmZg=atxBtm zu1;GUU7xlgtvb3TtuC#e;=ve0Qs<>@OBOsMq%|fNMXrU2&$DgX__St!1&s^A(B6>B zG{;qFFX?XyMfLbe>10};A0vxGvyx_qFGntgCdBBZNs(nK4IF#nrnG%L8ZaH`Bf69H z9H2N%Pis#*67n(~IG)xX6yeQjM`Q7s(lAU1)U=kguH=E#8EGeJZL=b!I8>E(I_+Fq zZ+KnWrL=*xYk~c#^ZZT8i_;aFd$*@s{VM{U+;-`1ZoBj#_YS=lk<)>0;zzjWviFSM zK|MV^Gd(vjfySj^QfBIja2ZMNOwUiBls+|ZHhq$R4XvO$(`S&jLK=TslGD>?``4uv z(Fl2!V?KQz@B)%4Nnes)mOg{<3dpZWuOPf8v^srV`bO$&`P4c^={1b0_cf$%CjDE} zx2HD+W^pXhh>Efpr_;zx>#1?+dmBrJf0PFM_ z?k_3DY=26z9%;cM(%g`KKK)`KnzBCqGLO$3nUM|YSJDSly#DR!gOLrX7coXYDw->8M89xM0ztO zXA}q55}reztO@MTh-NG#f08#+YSSyyPLqbljM89p`s~!rDYZ1qoFxvmSE!G1#?rvf za9PH39)~lQXG|s;iY0DYva=&4FF2h_U#YLk(kn7n8}?LXtk2j)7Sif8qn09}CSwls zQ;w&XWNgW3NLr9`l|~6~pfrWroo30-w3!(@GIsi#GInR|&DfuDFynCQ=8R3L+cP>d zj;9p+S7dZ&^bkIqalzk_(MSEICZj)4K%-MzXm`fd)V55)b3~?==?rYi^rwxdwfC&# zURqaorgZz)MQ9NbuxF;zOt&s`9IwDL$A@^;KlwB5KFOnSQ2;4?4Ms??E!>@@hX+nsPOBKCSJ~W-Jd!=zgIf zxsvVSK9ad8-AXnrPU~fU=CU}y9Q;+FE1Aw%nPT^!Puaxx3mM*!m*gj8t_|4Jrc)1U zinMcGBKw%m+yHtUMJ#Ech)s`FXI2wkms#&G%iIQj9rH6AK{qp<;mvGGJ(jsI^FU^M z(&o&T%p;(W5`7@EEAwRL>CAJPC)0YVe--e28rqvWkLuMMoKACYJ?KTCicw@znW?7|=@eSUC!fq5;7Cp>Ae*mcDH--GE9i32?xa0gL4RB7p1|&`h`%f= zm^nQwljvOLXXR&2O5L0lxsIL+`59^BvkC*VLX(rVtl0rIt1xSx|4gPcYe52C0(uG4 z>1{M)=4X{rl-8&3Vmh;u>2Ly_wSw0tw1zCq$d1lQS)Fn<(whE- zKyOc4kh+;xHIuWNvUX+d$!ZNYXSHP=O3I9k&+3Sr&N`NLA}F#>Wu3`7pLH?oa&TPM zm8{EIgOsa{qLD_VludjW8M?qDcCb2I&Da!fGq63|n;puIW@l&TWlu`lzV~8&2j2?)3|rO!1vBMezqhh3`;HyTauHm*Fqm5`A#`!9N!}oM!opX zoiyYAn#U#1OL2afXONunJcHy+WKPZ$?t?k$Inx8Xb7tz=aQA&7rzmG`&itH3Ig2wI zjHeTLVvw`UzappHzlYX2Z8@uQDs$HA&m#1@{+tat)j4%J^*P&e8ue!my5IDt3i|zf zPIFF6&OW_G^ydS{6AV1B;9QyIIR|puBSkq!a*k%XbEfBX<($kpopUayH|J7HRn#lw z;BU->Lca7{-l0ACXa03@@K20inu{HqLfr-Y3p@)?2mMpv0pLf39QvUlvx4x)4El$7 zO8p}6AMw2OQDD~TF*F|l4M_tp2c80qQigsG{5fDeryrV!XR0rNuHompoOfto2v1zi z_+3JM68ISKH-Q&Helwnw9|8wu@lL{_i`e@e1djc_(ZiZ`4)3}rd>FWbcRXrA{>_%L zvYqRqJjAp*2b_n%=>z91Btzg-Gi?q+rW%}YL1qs)ZOl>ELOv7tHpo1K)Mv0mGMj1n zY2a+6{vDFV;2Z^KH8`EfwU9ZQTbF@;T|N_%TNs-x;5c=gbq$Qo0@l72dYU2G&a|Z* z^1p&)Gg7|=`s;=t(x{ZLfuF^#U}|KnOhjrH__^S30N<~3kXi`-ufQ)ved~13pmsLc zA3Tq?YP5;_5ljm5|8=-2<5_(9fYR%UF;3DoR|2(x-twty{}jbpTHV z=ML5;n;?_QSoVVclupCi07^Mzei1utnbQxMKS5>?rTh-seg_*afPV&>2O;?vr0Q+4 zh4-{mL=_;?c*pEBsL{9h*D8vcO6EZ3?~uu4nxd-!ZO5EOGoqBSawq?yrC47&#U92l zie-A+fmT7^0m(jaZh{SM;7mi5jsrart$&(n)d5Z(OIq~!zZH>F$+Xsl(o3MH9Q7(k z3s&ec%$86c%TYV7%XXu_m8b<_%a8Ov1z)wY21_!$)C!$e=;=3n!gasf(8Kp?r#p^oTf zqDSu?u;dtI`r)@v!DA1h#C|>M;IX&rwMJ?o*T^&jrBpIsjeze$ob;n!)$Bcr9B$3= zketPQlLb2OL|LuyzXvua>+OqL{0$l!AoDjp2GADo)a?gdja&uhKNH@KR(M+PiJYn` zkUYr0h^pU%wuNZNPWb8t!Ab0(B~)cLDT#6sb;VJ^_vi zzJ`2+Exc$s)ru81c|{B}Oic z$1V-fIsQI-?hK8Msph|(_P zod%!Zh0@P~)2ELTddz_D$9(#I(9a=1`1TfV1*K278U5=N zG~c3+_RP0@1(yF2p6}6h>U{)yK970lE6DXb$h?5jU>)d#7|Ca0%;`p~eh!=`_1RgU zZTUA`C8*CGpzq{2vuK3mcW~Z;K4{k?9H|q53nB9>mXT?&f1UOrrs2KwoJ!t9Dm;+J zx#S`E>N%!OA?UdUbmq`kF>>C7w)+Tsl7CHBOh~-|{&vV)jag$eM!71ESj5$x#wwx! zrK1I}!at^ehflT{Bm5BeHTdnFNVV`gDcVszCZR1)pBXhhqQC(? z5qqm1;Wp^Ocw%0!mk$00w9CJttPSwnZ}f2(dH)Q_E6^4JZN(V%7;;U~ z`!!l^A!{?&fZw8zTCnqB;HBW~g+Hq`eygPb`U4mztKhdQh@~eW`2>YXmZ>Ro7r!hvA z!*BOM|2=45^drrNmc`ty%?JHnv`7i&h!SwZsMitZ(Att)>QU706lR}C^+*6+3Ynkk zBQ0X%&q)0}Ec}tqfz4lm{29n!gnXHPCkXyW@IK}gvwr5w-j-X zUO;zzY!5~ulRi^mG%(e`!!_{U5&!Q=OIY0 zMr8IPuM@af$~T&yB$^zOvEQTr3IGjS-u zJ52|l#eP$p@yx6hvk;FBxM$U$BpIucKD{3^-;|2TZ698R>FaUR_~Es){=6hM&Y@S~ zDS~obeUE}P^o zxrcabWUFkGhh&F5CQryy@(kWHT9RXcdpZ7?qZFGvi9W~Q(=h#rIWk}6_ix_D*fa%nF3W3vL&J9oKgoRM z5jBnI`%Syi>)%22Pmt>|#f?R@a&MP3+r|E1$9XFVNq`SOrIUh%xntbfHM6q)|~=tY5a)TEF6W zyJM2$R>z;HZ`|~c-AijWi6QKneE@rAKdiNDPs%OWExQ%FWgD?uwhOyupR@38+2?JK z*n`SU?14RrJ+S|YJ+QyU9@yuw2lfTt18a)#9#~Tz?}0Vl%zI!>6L}A;>FvA+)-*|2 zZupf~>GGOxbzX4}GPqQiodLAh6>>#g+2G~5Cb%ZMrh#AJn&q0~Dh8j@!87FI`C{}! zS7}@~^EodA>59{=J4P>cEeCKqpShe^S`1dYRwF%@k7>4r&oQW?@~ZUupzQUOzKLw) z^Jslz*_3EoBHSqNXyCf?KY*}b8eBW#b&uJ_ws1Q6C+^p(F<>W^nFyRO23(gI#B5`q zaXzlgZYne8n^@V2ZOA%eb=>RPKf;gejWeakfb|%#2I$Nto`5|_W zCfLUHiTQ7IEbL666Ji$S#Nsg4CWa1o*quJ2jM!PdLH%R(RY%o7cFpm_bSxj0V2cL;g`|Ab}!cb^h)P=yH|~IPOL{_?6dx5?(*2U;a=sgbgy-90BxLOV{)v2x~tuF zahZDeHZGUrJ~m#v8{N(B7WclG&*F9&G3q|xZjaZI<&U_Jy1PinN%!ekKab^cpL6%R zFS!Tc`&dlU*?rBUunv#a}-s6o}ee<$@Juc&i736O!dt06nbWR=6M$Ixb7+O zECH{~v%*v1Sp&IsG$!(RIl7I>hn|h3e>9)^zo*8tnd|1+I$TC9-Lu`(#C3@Gn^%gx zoum5p=$INkUX31?3?H#go?V_ju`$Hc>S^;F@^pBPc}~QARW)i{O{nXbb5Y`$Vu0tA z=M4PJaNcv#bJ=qxah~!FdR4F8>-C1b(Gj|0^DKck+neW|;GGP5nzsORZVU{0?=0^e zoQu5+y`|oz-sRqvsQ(SXyV_gD5Q{tSdhaHjYrR_%=TdKjcZYYUcX#645Cl|5q5_pKT|7LGah#paa6arj2(s#nk#OXqoPXF|MgeTow_HFQ0$NHPE z&R5Ujn7){90^d%zXccc8D+2Vxpl^fxF38`_wB^g7k1#EvUqHVC83`Hl0xN&D;wi@3 zZH%qZtU|K|zgL=S_0l<2eV%dbU1yZ8|5E=c-4ae!?uP#H$h8~v0;WyKCD){)?S{rUO7q1MP{(sSJh{`bJ|L9Sg%*}zeu0jUa7O%Bxe z^N{>I_%A`j1jr{t=DU!22K4ViUuN3A3+;urv!P8@w5fF|=t-a-0euJZZbRNbIbg*MtbPA~sYrJoW?V`A_8iB=Y_U{C4oqpmtH@b%Q?}{BMKS zJ^TgaJ&U}5fo)U4kLqIr`UN6NeFFT&$g4;G0nq3X%1_`Kd^-0qhblh!xDZP37OsC+y*Qd3qAEb zQeR}6#ig1j07WWykMobp}A|4S_L<=3a ziC@t%O*}2S#O>k)9q*Dqmz`pUdR+arDB!o!MIoLtZxuxZ+eMSuCH9C`(IyUw4sncf zbcj<#pAqN9MR8eN5ra~dcIg!-WJpG3HjX@k334*wX>=6GS#pjnrmKarR4$dv1od4=G;A}e^5 zGdPU*fb&hJWg6t2;D3&>`C-s=ATt$sGw=dvYX`mD(6bNEqWAG$JJ6efZ-&l~<7w(+ zC}j|w$M98>A;u;r=;u)4laO39{Pfkdfm4-_s<#qt!87E5`2oTECnNmpKQ!D1ybQQV zr;)c2Ut~LmZ+Pf2ut@*nrj@^tkxem>EAqs6kuN5Qw~4olN#Y$80q+!3sdZ;j%gz>a z#9Z+K@j+26?iLHgJrqL~VzpQ+*5S8>UrT%ugru?wiVWn#<`Y!ZNM_gGSw2%&RJ$y@-2m0on^LV zo@D`*Q*J4tQdsK|(hxr^ODu|Ulv%9WHp>du5<5tTepFc2XqA?A#M#KYh_110w#*}Y z%1JB32Fq5&v_{&1l9@G z$<$tTTwAh|sTgqmIVVf9U9SZ;Sf`D!e*{q4HcP}>V4Y>1V=azbm;ht(nP*)%Z25>< z5Z_wLt!x0>KxM@3ucUG>(fV;V;pN&TI&;cety7wy-VTHqJJl;gv?I1f99y=;SVbkZR73p>dx^HszJy!JUSPf3@1T+nZ>G&$X|nF~jh?R!>&qYzf-;*jw#w_Cwr@VmzzezK+W7V6Y#vpRk{@ zpV3+@xp6?>wszaklU(iaxDbZ}mPr8gd;3L#%l0ew6C6ACL5GTd7^5kQ$nuL0yTfZe zY`0o#9U({5k?qKHOo&H#m!*(o^?rKNF-<#7@yl>dk2|f`*6S#6%yP`3zFkMWX4`y6 zF^{&6g|=&sg^p6kQpa+~O3PFp56Qk}?Wp|}m9(11=WFDb78*?^k-R<{Q~Nlo$afKL zd8$R1cFD2cvB^KWvwFiw(j(*2g_=N0s3Vo#H2qtQVQ*-i{qVk3YcvYpZ0DxODTVG+! zU&iJAcc3-kZNu}lG7a*(@zh}#Qa3^VJUDn)$y5SKeI>C4{1uSg2~IWW2pS}Xa zZlfyfLi#+{#q=w^?y#y1xBz;Bx`ohl5;7O`_QJi$eB7PvM(s}M9-tK>wb5WZmDRzR z%5BHWtR0?YypFNyxb6+K0-he36qNfsEbK$Q_Mu*Vuw)T9NY%o+=W!?0!C0LGdz$n( z!Oqrdox?sPpR=8f$mN8cPH0}u92@Foy$t^&PPF~-c@29;DQ9ds2YxBqU4W03tJbea z4s>?H&!-q`+x2MF|2G3PR;8vjkhkLNsJ-}J>IU5&@b%SghaQ>6-CiTwv;?igoC37S ze3aFudjQt%gO@7NQeMzsf}X?B>4zmp^}Yidbu?{-wmPhvJMhl~Vc=EZm!iZnwCO77 zoHe{|HeG<87JY>cTxj$=8}C(thbJ1IWS?|HW(n(8FY3MmXEmZ?q7fnZM;G8N;536? z2l-rmmnd)_JcIAgs>Xc{BlzarRU<0!A0~!|6X=^K;Lj6?YP~Gx z95v)m;0fOe+^MgGjJ{v55wXp6w8BegSVnypEO`dh5CJ3+_Nmnmzk`d=Q~mbkuXLOaCzjKLdyj>{}3SV^#&pz3A(^8d&=?qB14!}fZG zbG+WKaK6sRhM!*PJfeTuukUZy*&oxt-Z|WNHWAbsbnUR*h*;i|Ak$#zFLlPpYu65f zox?sG5gUBoJ*pq>CD>1J&^cm^g6v^}PUmo6jGd1gI=Y=B$C@k7;c>o);H*JkAm}6L zC%Edwxb7A@&#e(S4LKeYCy&vFF<*}6Q(11mQMZILV(IR%UWXB5;48)2sPXn?$E&f% zB~`b{oj%NWk0TgQFfqYbHyVdxz8^KGj2Kf4a8EJ(OyHhQFw;3=o^lrv%q5sVN>}zU zxEB#DHt1z~+r%NJr`$NNBB&%-tJnWV;NGA^EbiRZ#<|WpVlH*p6Kr#i7^mHh#<|(> zqu)7VZjZ%*yG5@r>bQ^KfRV3#q`%xp2#ykT5u7AAO>mB&m*5h?0KqlqMUV0-#-zl4 z=zSIES3A~S85M7@QvZmtQN8gP6mRnzokL^8E86i&WA9l0%X^vg%Z;Il=ftsStn+JW zpF}_1V9p$~{4vh4Hc4DRv41^QXS~08+Ut*I!qLW7(7M9PY!a zbL9Fs*8gH_<5>ATe$zbRnKWzz`rbCZ|0U}4Om&XE_BQl-W;jRo#nEGwF&23Wog>%r zUcDcCW)IiTGcRG>HRL=CM#YKYZ%>JHM9jv0%P2KZ4Y4&|HgLw?JD7QfDD`Ep+Y#zZ-ZXIDZ6of#U(L1Xh6m44-TVUIETyz~w0A zL%{HZx(rzNVG#Hi;M4#w2F?SX4-EgPABSyv9dC#IKLSRoRrkXWkorl`?I#ebTg2NP6CD%9WXDCvMez>jVdu}p6z5aUr^Hm3&lM84xx%inxZ@vQUV#vD13|Um zf5#BAo?sh6BSAAki*db=;DFHoRgoSFl?2tYPS(@04Vce5)o5HTBdJOwZ!_g>p<|oemv9`o@t5CTk8i%=+ioMi#-@J- z?)q=Cz1-K>ZuAAV!LycnvizK78e7hLga*&*I~(zJ6yx7r@PE+8|Gj8G0oMQBWfgD} z_Cw-dUszB4-(!ME%?6GCZ_z#hTm}3d;78G0dH+e9{5$6$+=Sp@Zi<@03 zt`sr;4byuIKhh^~jsm{{VdI=mFpgk+0!$>BLNMKcbOUA@=b{8%EIU{5f7|HbG<3&+ z(-yrV+=3l9BaegEabBK3)*)ZIYiO;AeUnh&U)lx!!zK0|Li~G@@nu4eTeHEh0RA9$ z(*6lFB2(AK&yioc3%@jom##pjL@2USm_>;gCyM?F|3FZLS;^;LXOu}a?wXa!%DqBU zHYuA#zWWyUz2dFZGG1z#ng8@w++(V|MZT4S_%HImgh~EQ@d&%y?jA3)grZu7nF9Ks ze)=c;6aMdlBHl0bUqqLPG7-VApjl$K_`b*y2k5v-91$l(o;X9tJH=T#riycP+$R1& z$29RrI&K#)&@o*M(D5#DMf^?75PzqmK)gi9yQLx(F_S-OCf*}8TCEgHtF(#tN{4ia z_eqy@i6ZHd9&xAiNuQW412Q1)l1Va2%#q15S-fAS$P_VGrpi?D0huP##5|cHGsFjF zmdp~xGDqf!56QR4w}|=jCV7*%Tiz^h77J))T`lh6RkTsWDR+v+w9;-CAC|l1F0n-Jmb=AAc-<|Q$~|(Acu?+@ zd&M$ddy9|CR@o}blX#qC!3`pBA5zzm~rit7W(B7N3?U>29b} zo|31;XXLZ;S+Pd`PX11ORz4@66IJqg`MmfS*ZW-W6Kh>xblb!u@&9MXjuV1+^NwI` z4*v@7?r`&t29}75;eV2ynUF@ZzAe}3;FYp;d~=kJM2SL3G`@_)dm+*EGIq=wX@{o> z61;|w7$C&^#;naqnW8bR9Vv6?nD&m8nLVb(>4qHcEyP`8*sM@~U6Wp~Ib+y7TIT&@ z*gRTh?ieQXd*6 zb#hFp`J<#xjVX2aDC;Xw!>D1uG^}4Rrq4r$HfiX(XAJAFms&W6_18U&+)?Y7m{}|R^FI75*_18;1FoyN~-Ujz_6G0f??-FA1 zm{D=P+=s`Ei|gfxbi# z(vT|~vt_TB``DOmd%fK9FlKaFM_KhXCVhsDnlKbR! z_Ql?OnJoCZk5u^0Sda1@<$KEamA%Rjl_L(Dy30GS-BW*0a9w_8a7zLuka`KAP5-aNX0 zu2r@u4OH4rWjA?jzj9DHtaK{Jm2Ra+IjdYy`sm!RT%~}~Oiq*E6gH)s#+k;OCYq+0 zrpM0l^qHn2(_GVh(<0Mi(=t=JX_cwcwAQpCrpHumsx#G_wwW4D&88O9KGOkHyXlDO zsHw|z(sbH%&eUtVWEwDC?<-ZdaSsU8B!&`(kIqzSub~AJcb(^Q-lnx<_qQ+tfp9 zhkA_ab0X%i7+<$TJvG{2aX*dWpAmj}mHvpePt4xL@}TF8dS1P#UXIuQiaKak&33cb z95P4E+2%a+1oLF`G;@Kv#5~J9C)Q5pV)H_Csd;IPZ(eR*XGYRpnQ} z!LEFPz4PKN$on(kbjaAi-w2F7!b6Wi1NOPg&jTZu*bF)Vx(?V3d7nORfP)>)0()tP z@UDQq`&fb_P~V{w(2ug@w}9V+)ICUr=D}|;e(71p*K|K%cQ&m$SmtlQ*zYf~zgs>8 z3`@jX(1(EGEAcyE>|qz5Mcyxf{sr(<;P-=nA4>mMl>QxPC`0P+f#HoI>_bT{RjU7{U)TY1I8O0;>+OcZzSN23Vs$&{I7wJcQ52Kz@GviJGD)yyJ;L` z79;gvKqHDI;zv#b2YD6!EegCLL3hHO_ZsjAk@`cV9t4f|29%EjJHY36DPT8dwmkHa zl{6=qM$8vHQ`mV%QNC`w*H&u#h;6y;|fB1BQ-Z!kYde!U}2m>Z{C` zJw`r$Gv$Ahd-KcYj?L%TaZb|puW>Hq@|tNTANdtm{)hP^#+OHr&@6rgUmtBA{{ASZ z$p}gFagq*x#xX#=Oh8;4f1^}%$=vI{Vj9;WClNhW&Y;wGSxBjD1Iv}>jk-(^;ze^-AW(vF`BJf_0z*|HD zBZIys8N%B$5+i`bn;iOBB(RTO{1{k&bLfQ;(WtKzR~c=?ct=8uM{tiioURC~@^$6w zLg2SAseMZOlu)d*th0oP+JCiBsm=REwqt-=KF4#}^P(6xLTAXhH=6glY7Ns0EFDMj z>#BDgX&7%j7kHRxj(HkY&{G{Z^jM}LpP0`0`2-)#BOfdnA@|?6HBtA=ThR3eTF`Y} zOeL28OCvRx&iQmV8G2ct{!iPDSL74uSQ5A7jVWh5tq&&RSZX|BdZS9x`^~&JBxdy9 zQ%tb%4bz^EZ#TUD+9T8gnRLu2So8+!&%{^8Uw{2kq0y|$#{zNSm4 zuiYY7IjNi#6P0tclD%E4&?>|oSkD$@B6Y+Gtu zX8V}!leSg1hi$82L!zhZ3{O@34{G#}vN+ZXn~hfZ-(WMZM<&sVBxG3pa%;R-t5aWn zl*Lyc6|ei6;MH0ZTc0eCuTS1QTevS}j_ON#UwdsmV!`XsBVJp%vAACNI>hyBuW=sL zIr}=V#pd|eR)^B~Tw3uutubD0`LWq>aeOxXM_cepzU+FFecAP<`?Bjb`LZrPi~sML zn>NQ+2>%;wjdn?3t2Pq;gO^K8kd)%qkl$_bVkPf_1IYquwp66P_u&IqRUs2~P$7(H6v>8^ogF&9LE(X@#ygy%oCtA83VF zXM_HXKDJLYaW(v}f`74_LA`aAI8Jo3@~@tDsM zdyfBd{Fj)6wZ;2IGJiuDAdK^9(2TQcoJ|BuLV998^QgQZR9CB2>Ux4rYOT6OZBTcp zI|+8Hd)58wLG`fONpM{4R(sU5>IJorpkKXe7G}-tH2Vp{YL7YHJkC7cJdt3EdAfO~ zxkx>3o=dqdnCF`pnHL+9%gp8GRpv^f))H(mSDWib<*PSuGdB`6(^U&y?K2-BXs4?q z=A-5=^GWk*%6ZP*OFA!+&P#^Qdh-D3957$g6wNwPr{>mz<^e6JMYK#USIakF(k2m1 z)n=IMv_fsRHjiL|7F27s5|W!outY1TwG3|tQ%3QDRFgFr3YiGq35#gfv2kLPC=3T$7Mn zNNx$q<^MVF^KAQWMy|i~d;Pc9_wzZ=`+1&o&gY!ZxjyH7&S{ESG2^`6*to1WwrMsW zo1b3FR>T%SzhH`I@^!EBri<5=txQ(B*urcTa$gytwko#j^s8%Bvo#WNu{E=`wzU&+ zvBij(*%FM?w(hoGxYsZLdv``dY^k=vwqdrxMw)GuZJceAZK`c1{pQ#f+Lq+jiQG@# zgihqrg6rn{^>X@->A2dH*!rB*2YLXl+sG5;sXi`qYRco6|5Sv*4vB&g>6x5 zos6_4j`aZ2N~pDNO0J5LYXD%QQLcxvENDvGpK?tL?Ef*uQ?4wM>l5UvC&di}D;cEK zQn}tqc@Fru@D1yWlovqd>LV}vj;gd9+89)>VX!QW;x1O~Pz%JeR`n?TfJp0%Sihve zcIlNzfMD&Gw9?9d2L2WNlq+@+id-RPSwEGbKaOYMvt`4w38-94Rt9&mnnGOzDr3?R zI96qFtml?iJf)3HhP+VaIv-e?RHT(qtN^hrx&8vYDyX#Kif1fan9_PHtgAA_$Fk>& zwI7yEc;tw*WGU@*Dso+uTs!2wRF3E_$0|D}^P-`ZvjRtaG^~zS!>^oX)|wd|x}l*gYgi>HS9wX>v>H~UNejU$ z%8`b3W!g)iSl_532UU)@qz&NBh)*iwAZvy*h`F>tEcF?BNM&|jmYrY)tLCuoQSJi~ zBTD2a{d#8iAa8Dkl}i1)?v9>T-?TcDt2h|zb<;j^;gjW0FT}5YwokV*XLakL_hE9k zL&JY4{Gwi|C~{>&p8Jh&J9YB&waFcjIzN94u&g~)y|VAnbH3ZH`}HABliLdQYF`^# z(loon{$5L(9d|{V9d}6@xwobeT9Yh!db2zAl{zc;#WzyX8u09|q`m*l`z8vBj z?*7gF8!L}|sBnw%kvm}8;gqDI_e6}u`Ua8GgYSvh$owjD&*fLqdnmu;?$G+#v5~d) zt*3W1%E$0KEZyzIN~U@YO38?OC|+6Z_3hP4S?$oB^DS%tr`!|J(DLskc58jXtUM6s zY!!tSr4iXa-nl%u)p&_7DX~-riV;=VsCy`82J9^a;9um+n0qL09NHmjRmJKV_Pr(l z{u|$r#V*!B^c=*lloq*SD|VfvQySiTu@*amMZaztxdPdMJZXHlM;`y3d{=W2KbezapO5Ift)cMBpl;8v zru^T?(K24rD*w~6>MVr*#X3dN|6Plb$NzH9ddn(y+YSAn&f8G>52UyWo2lzDGcxRd zSYA;tiJq&l_7Y~T2kd*@IiQ>E-`+?)8NxiQ8a*p)oK&{v$*vs>nf`YxHbTRNozir2 z=d4S-R%~v4CCJ@KM^AR#`M)aW8dhG0GGT*w9QmKM{_@|E>Nzo2z1EKYJJP)Z{fEKw z&IG!yK;z=Te^JT{OG090)MRp3At?fGsOGr0a-fQr6#QQ)gU+hbe;8?x_*NtA;C25S zExI>z!dnKFuKtZu=+ur@qjn$y^UhNVsm78mYXko#ImB#@@Vzx=YntWhf!#`&H@f=u zZiJl4ZhX8LX7$F(SZhxE-jROKjY@xaDrXD#!L*6+m>&WW#z_1Hu1`7{82E~FhB!S2+I)7`G+LcHS6|3Bvcm6*%dCu(MWnq_KZuWyj5=QDAmVm3ugz-m5G`pE~s(PSKor{u65@ZYE80 zRy+)0|HO)in@QEIFZsjSKe=wU9|SDcl9L5y>+ArS5P(0@THd&l^nTnTV9>FoVI ze-!&Czvtg*ti?{cP_(3?zhPQ-yQ6bk?sla33ftW`eQJf+wRsD_Cb`bqjZ_k|Pt_G? z=_cdU+{w6}reqK{Pg$fa1D>d?Qq~fZsq9d8D+iRL%1Py{a#2~tIk$6@7vjZu5HHO` zc{s1ktMS^rA#cK4X8quw_zC~;<8ASr*Dt@g@n{~$lPFw@mvHBbmxxnVe6r$_Pdp%x z@E7mVGby|;;*_kc;sbaZAIeA2^XbZ2K9Nu1Gx%&ipMOa?v6`>X6&Lc0S$mGH-Nte! zG4`r*xqDdddEE;HJA>s;N4W!7?)tU%`(i(_f^!e#j$V!(z1IF+>{?c_XVKb~i(RA~ z`&s2)+bh^ltc0@c-HjZlJ;>d(a#t?Lo=%RPqv|=hu}4;(U|{VK#STa=_Z(yYsNDG~ z_fX2+x8h_Jrf7a}PsClh&lNkNEgSsuoCLY^Snk2ao>6NjwA?F<^9`(>vU0CB_V~)3 zopMj7+@q}G90`62cjf+0?8sE*-fQg3z|PJrOCwpk0AFS~HuE`u&o|4ieo*!k;tXS* z@_Zy8#Yg*|&+}Yh_H(Zg)%Y;}4j<0PWuFT(RCwavrX7&gJ;lPx~Y;u&7|=Mi6IU1#xSUR>N4Ck@PF znQRB~-NX-M^Q`Brbh&~vF0Q~r{}r5raRod3FQ4{GmoYizj$>!PEUj0Nj@F)jxyv6q zc_jhwLJ#FW@+;T@EK4_~vN(sTK1M_$9p5F*5jA=da!cKKH%piJSVz8RGf+y4-Q|h+ zDb}wDc39lVFOem)KCD0WHDziHr*!RuR8J=NG`gRGpGe6L80oScc`sv+^<~r;mrx^J zhCPEzNT16nN0(4SFGKcAIG5uR%J^l}1DCKz>=M%O@=TWBd4K)0TjA@SZlN#_^F9Hv zKfC^U7NSPEmAAfO^0X+M%r$>XKA1E6JS_RkIu%P;s7<0f4e2_C z@V@vB!1xh4b$LrpzF4_{UGJA*cj5A~oSc$cdKoqRW$5Q+)RvcadgXXC=6${)ED_M} zRiw?^f0G`ZY7lw0S`n;hQCNrU_pepkDb(M(`5oLcocv???hVE=@7n|L_K4ehd*p4s zJ?ggJ9(`MHkG_CSO^>=t7#-+t$|-X4Ava))@yJ@C33 zo%5b}x&X`S6^Pl10$26@bKB_383Bk@xoz3hn|SBOo)f)0VZB7u==~5Y=^kux(Kj4r zxyM=D+bwz)Q}J8@)KE!OGsRJ@ltNdcW*UnBIQ%DJT^lTOSnaPoUkescvd=~p6ur55^=sjMBU75?>y;>I8)_AOv4wNVpd<^ZiFRtTlN89D}ldHqXJPwIG7kUnD{9uOVA9< zx{eRUG3LrI;z|7pqIM?dCB+qIexi(C`Q^%b@q@4DT&?hyexU=kaNZ|aI+^X_pOTz`({`xh)nEf?AKlINZZVl^u0ke-;mCa$~T@N`Jrbg-5^TocQGxW{9crA`Zc*nwcwvcM9QqoFAXl%xh;JY z<79FERsU zyZHfrl%M2h`9+nhcGct|Y9Y0l8l;w1L)CD#vRaM)E338DhH4YFrP`JUs?lnknxv+v zeZ8p_uB0+tQ0p1Ido{hjr0M`QO&zL^P}9|kQbvj7BcqhgTP|;jt}Ba7H!BYC(|T)) zIzydp#ecr~rMg^Qt*%$Ms5{lY>LK-*dRjd%Q%}9DX_}8jG(W9~7N7-dWwfw-@(v+p zwJdVpjpN);McOn&ufjS#Q=Tu0xibalbt(@CYR!>h-b%qtmUWt>v@eG-82b$rr(P;J zwNqMAQ{=qZ0Z?g;O#u$;d<-GV^GvO?S#c_>bv~v%r4;9@TJvkK3VH>&_gSUV8kr_x_u?=yPGjSXeDbl`N4Y;4cTaZ^;5#;h@Sj5)*WYVio5tUXARoKVlID1sV zyrGFD(7MpX`!wa5Q$6sikJ=LIN_8_%Zk#@ zo19~XWjT&BKs8VmJxh+6AUSKJA{Y1va3fVY=7W^ocnj7wRGc5GmIalz2kYTUIV4hSQx) zqSCKS;SYSt_JhL7V7m#aOxM+v+Db#E35C>BX`-|x4VJ!yW|bdlb5I?YqtkMBS(Rt& z%6a1NbJ9y{xzt5jilpvJ4UjhVENhI=Ma#yb)Gf?a=%sV?%$n0ZoTJSuG)%=Qo6-`W z%x?{|bn5<`ywqk}d3I+`J-xU8v*z?l-BFqU|D;zz@61WBS-BFHZ9_)PN=)V-@u_?| zpT%eM4b-Y`=3Dtr_i@M;L~jJ)BzuVv!OSp5zYaYH|%J^i(C7*bR zZ-ekxyhqRAU5ZoIcf#y+$nqC%dSA%k9T&Rx(qCaeHJOQZjwx{az`Y9Yws7}`+XeS( zxZA-U1GfqHK)4@;`&+nQfO{d_jp6Pi+*cj}{YoH8Fz|kOrU2aY;BE-_47jVqJq~UU z+*9GM3U{P%Qz;hiCU75udpVwI4fh4mp}0EUnGrQ&zb)cx@j?xnsMIFX6toEAz(WdW4eP);c zUUeX~#DZFW9fd#eCEJfsss$^feJ+GH+N3hK8}ul()s;nCtwgh2-&stz zE5+vJZ8aTnccj()e~q~jjhU2Hj>(@%*VULwh~cEXttl+<-F9orr7iV!IU1wDra70R z!KbXWlA(*;lMS(Q@&;D=Tr+0Eo_p(C{{6i~a=bb!Cl};6P(d$;x5}mc-ea5hZhF&J}1p@NxC1G z)&I;+ou#1oW~!j)sLJsS#v9VYx+2G*7$YheeJXNfFUK%)#m@b}%GuBVpc&dft2bA_jjO}c~JfWj(GzWvl()%DQ9EkJb?OA zPR?;T--408iZxGios*S+C^0JhRG~5ISm1KKlw6yI`3F^6nD5|~PZ)z}_%^0L40nxx zHqWwiEL{9&=UI3@GYMh@mPz?`Ri5RZNg$*)y)VmJTM7v|mR;I%W)ddkXffaHm1odm zxpK`)>qTC9w#+L{cX;L5Ft75Ik~hy}+0x#~$vG_$?z1`h#O2He)~s3cJ{S{fIP*jM z12obr=O$S>SB7Xy)cn`}s(f>~DO$QMH}%WO^{jkfi+LlFS8H>Ps&i`CoRXT=&$zmz zE<#CNOPZ0@XURQ(gkA_eyMqv~I+j8TLheUfzT}itw4K&mrd;VKPrZ<9Akhm`Q9~;7 zyan`2tl1)zV7ZRbIz>dDPa@A7!OBKCCuOZy#0-&!GaTf)K^3*D)fXubindVB8mj2K z$hj>II;6_EKzzm4N_mykte&Y{>2SS#&)qY<`aiooSSvH-s>pVCG^|jI2W7W(=zGak znzBcA7sHZ0HaSZ;$O=nZADhW;bp683l^>xS$L`4Q>e_S%aasR~xzttOrHcC9#l+lJ zcDdS(p7~L$A1><8oZ7UuXd#u_x#qmc7cW1uYS3tU@}v@{Bq=FMUuA%jrVLd^DCx>X zWr{K*yIf(73zvP4Xs>jaX9>wZ3VPpi^{`wwEKl~~QrF+g$tPKsWXV+VEnc3aBj>%X zno^czIsYoZiO4>RtU>V|M3vtiBCI%wlWUe^l$Qc}0_UoX5$Cz6d%a32=H*l=mpr9N z`ixk~Gjw<~d^paQK*o89!&RfUo1T&Xm(B0WwSM&TQ5HI!Opi*Aie^C_UrJ778#^a= z>cY;(b&5__0u!TBl9VtOz_hwGn$fu6nMQ4bSj*;(YX`BE=b8~6!Pu26%$MoRhw6b6 z>=7EzlxMYAE7n%5L#2C8X3+h@RIWqVV^n^hq)|m{_5$l_-L+vxu_Swx>X3>w?x@4s zuy$gW6nE{+#qMLlR6CSo5$q|-ok-T6b+hg|n8}K=2U%Gft5l-AtH+*a9awj)f)!7Q z^&w*ANM%-^J;Rz&&C!{?$a=At*#P!>_ET;#_F@mPhuGt+0c*^fvyLo|C9~eF9~;Qh zvY%6#AN3ici1J1)>Ibn3Et)+oPN0>3aOO|xRGR9+hU{6^g2k|S)`Rt7udr112A&b? zP71K%>|yo%0wxF>cqY_ip?x4Lu`+=r{4(`~oYj<@R=s3`+pmRW%fUW`E zBB-W;nxI8v65~5-!Jr|a;hBYXaI5v@K{fXk2W3Qk0eingZGvbO2~t zY;sgbZ7Ap%&`F>(Ka|AfCeO! zct$X28PG7$3ZPX$tM?$SFzSLf0&ND`8nj)Ht{uA@F`!AHeLzz|hxX{zNea(G~$(6|^R3eNZvuq*7fI%F5n^OJVn2hide%Q(fe93vRjUQ}l|8Q2keq>ap@v zM^>b|t`gN+k5g^(1l0piQoZ;T)o(SZMyN&gXPsPc=I7#6;}*FAE@r+24Y)3KUWckw zCl+H5+=NR(y?kxzdmXA%En9%<$3lqxtzIeY;ps4_%kucU|gtU5c3s7I+=1QVaA53%LoGb?V(UY2bBf&~<5v>rhTTfYLX> zuNTJiY$01see}cZJoTnqC^6J$9z;Fmh01DWhjLgs&+WV@FT*SI`n)yY#!sp~YB4oL zt)ezmBh>`8uR25>r_N9pscY1o>QVKA=A#wULbNJcLoHHE(E4gaw1xU!{iMMRKO@NS z7}bp?MhBz2F~AsZOfqKMrrPG)R@q0{r`YE?$~Y=J>N{G~c&3kIup`|u&9Tt2+OflN z*m2&+?o-sKj8A2s`aZ3F;+Rd5`U(woEVOi1TvM;bCnEQ?dz{xbz35+>u9cko{=szZ zpkE;>_ob-rt3Y*6ZRvLbD~tjji^o`eoW)m5UXWY7nwQ@cR>2hbopaqc&;35Q=2R;d zuzcue@yS=+S!40jx&16uAkTHt+}HOF%YA+SvE0`I4ZW@@JpvMQr%&;jx$hTWo#+06 z-1h@3;Jy>KF2blzuSWgX#?+r{M?JbE>=_~{0tZN5qL{_2Sp1~qL7^7kD|t!lxssP9 z4~~=k!Dx%`lf2Z%yy^CkO9xG{^t5* z?(5Q3@?0Ozef>ymp6lrU^7X3R*C7G9uR{a#T&L!_-k4is!q(@x-iB+9TEZfhg%nEV zu$_`W7GQBJw;oHv)5WQ0P@hH{(nF z&8J4`Pvg~LYz&*orm_q+pDkgl*m|~&?PiDAaT(iZS4iF@(Bc`AH?3}QD;1krx!5ep z;yWa7ZnX%_trnp9BFS4=sof&c;+B@Q_)_xcta|IYJ{Gt9YgyUiR!h?IqU5bCZErQm z;!|>`dTT9rDzy&Ab)kG~+WgW*`r@?sP>Uasyp1JY8!PsamX1YQQaxYD;+C|}ACtVT zl{#&$RDQvd;svV=ybhtj?O!Cw-%ibc*T)M zA#rA&>k+wSh%b`oIyldD!#vkXd9KgqzE0?yJG_J`x#O3Rk-Hv9SeUz9U3EP#&-Ls) z;pHjU3Cr_@zb5x}m#TTLmwWB{lh(uQ!ly>;8D*}m3O!RmOA%ibsQ=hktVh*GGH$PF zZ^krxYkO-}*xuLPj}@`MY9Gk%x4&k8jRn}>u)oQQ+lSjHups*h`)O9z_YvPmSvg;i zuZLCeJ?49iRdlX!u4EC;Z=K(=$}WFb5%##NsH-TAFvaLVhmE9RY_-D0E(_aCn^^xE zpad%+O1M%*si`znnkkV=w3487SNbRel)=hy%qmY+GL-qs60CsRqU=%*D94mD^yV_^ zLvN&B^j_>BIICRd1~+*T9>`1cFzk`2%^UF+ye*I62|Sti<*9rKAHf26J08oEcrV@` ztKvrS3498liCq!P`5KU-J$MP z5348Dv+8Bd&`hm}7O0idLbVE7RjszxNNYy@`)Dn}HlL}KR-f7yfM?hig3q#j0Y2Nd zi1?@ES`7ZF?Mv_s+gISTY)imr+mLJ3(9>Uoe`;F}o?-h2e3oqm_-xxsdOE|l zimqnS)8B%BYFiDSVfzkzmTe9AY}@zr^eo$2x|&T-uLJ+owjMmgwgG&WZ6o+>+a`K? zwk;F!pK04{$+yLlZ>uHWHcP(k^z=;I4ok`(Ea`Sy(*0;jx66|2Crhr~mRx%*x%OIe z{S3MGS#s^S<%H9-ABr0_my(lol-8lOUh+8rCfHmkjw5T z<+A%rx$Fg`T=s%eE_)#4IlB)6)B@nZZzY9Z=Uk3N#x@+9idr?R2+)F7&r)7fM;llm{q z*c!HldUc1{X}YtI9b;#yVewH4DS=8Es;8?eb(JR0&D2iRvNzR6IJa1?k>HGmM@!U?!HPZQGwtu^_{rf4~zunpX?aB6UZ?=CwXZyD=+rMAQ zRnK0Fuw#~Mr1Lkp>JWC^a*cHE&kpxMcDM(#!#$K8?l0Nl9?lN;NOrhKv%@`+9q!5O za8G52dpavz(d%(8LyIkXYXQ{T61FNTOb2 zSX?+d?m>Y?_dTpkSDzY?xXAJCoZmu0O8U zgZp{mcC{l~MQs42HSRWqI}&$a$i909KAdv3BIF4?^9TGn?P`lBTZ?Ce|MX0xxGVgZ zclmkY4?ioy;=c3RHuJye4j$Ot4!OKoY)|(Bk9g<({O+MdQN~az%imRd=t40YU$SBFU zvV!eVkE$i8pZKU6Mm@s_HB}v?zOD{dhfsg;9d(pCT78dtg_EgYI9>fj{ZyT$ex`n| zexZJ)eyy%l*Qo2&P3kuF2lXfQXZ0ZYbxi$@`iH0WGVUw-lloJ74ZW6LTd%7(&>QNF z^=5iYJyLI{N9&#SE_ye;hu&L%S?{k8&{Oq6dYV31AEFP{hv~!h5&9^7jGnHK(drdO2F_=kO`I*9 zt(|S19h@=FM02lsz&vaoGmo36%`@g%^MZNV&E1;YaNFHJZqr@BUBq3+9qRVDE4VAW ztGYjSf8w6yp6g!VUhH1#{>J^S`+N5~_XfXsza+ozekp!^{QCM0^BdtG>!0ACnI1e zX6N7$^;gQl6Y43wj2^02)vM{%^_s}R`gw9NR!`8A^klu4-uEVRaDJ{F+@NpLH|yK2 z9Q;N9RX?HsuAj>%2k$qEUn2*@jS7^5Rg7vzb)zP7Fvdvs{mgf+@8@}PFwhyCor4}{ z1!raCU~Ol8XQMnhxX0XY{$l=W{$`#se>eXy|1>YT6}Rfv-8Q$w?Q;9O3%eh7Kk9zW zUEW>E{e*j}dxks1J;y!Yy~w@9z1+RZy~e%Pz1}a*ud81-zaD!C7O`!o0F?l0V5xxaR=bbsg9#V^^fm)~%|QT}oMiT>UFd-?bE@9!@xn7jwQms3WW)|DA?s>G$X`Rry9DTc5+a6QPUsv0{ zS2w7e)$OEhd!TK(tG}x9UGw+IB?$O+Tgoq5r90 zG898M9NF4dlC-T0w5=qxEta%xLB84+?u>9&aaMEIbk=n?bT-bTZ9kg_%_HUs^PKsY zdByGPcDoC@?{h!mF6%DmuIPT;{iJ)Ed!~D~d!Bot`%CvS_X_uFzXZP*{d)Sn<2Ta3 zvws)=ZvH*}U-Ey&f8hUX+pTFEE5>}>XWW0t-k1BV`u^}`P5LV_~q>p>`OH@ZGR7>epPdN-9XDsF5 zSImb-A?;|Kn83PwjUs%!>^M%KF{4~RD$XiYU?ohf1QcsP#SE~R-4!#$bC|ThIiG$D z)jt@gp3ym?gVaBX4p#pnI?NbMV}~b=K181~`Vy^S^dnl!eLv&oX*Z4h%`@s*yn7z+ zUckE-@$Q>=_a(ghGTwa!?-s+m0peYwKc0LQbO7i;&{WXZKnH=oE~xW&LEXQC9uw4> zUln$t#A;H`3|7E*Ez_xQK&hx6&2bwg@_W`#&v`b-jc4UZJpguDRl+x`d&C?qAsp)) z#i_^<Dz_lmD3P5XWSNXfBr_NvlK)(SEkXepHatnniVn{8_n4RMoXg=D`~Vbo@WpGPV=3`O8frhWGu|p zibjqV+`;bBtg8Dd_fxEfd!u_RtLfh5KFaF*X?_}O<@d7R%dEBEXur{{jenZ|NZ4Uh z)DJt`Y4b$-2|W&$;|)1__($a$2Kpb+pFsDB zk(j6htk{cltpkZhP(DYns+P1*Tk*dUKWr%L#fZ*FiO*W!axciAf2_sTMJw*XH0~5- zOP;JMrElaV{oncEW{cP=s?kE&A^M4R_j_3;xpq>mwhAZjd+w3c;56zyiI~p?T@3m? z=w8sDbIJD`=m*ykwpp$fgo@QCRqnZT9(WDe_uNzpG-1)Coe|W=MzEIT?nmu)1hu-g zSzF815Hou+^&97t`gbB81xe4>vK^HBC+Rm2`}Cw;lboxgOnhChe9x5rw?W_as<}V) zN{Kz$a*MiL(1ZC%U7hs09nM^?KpNJX^t>+mJvmq1SvxE3pY^IYZa-|uOTotFwVxIz7db?K(tXi=-u#TxqU1%(u*;=G*2F zbG-SkIo5p79A^$Q-!X@q>E=jtggMF_ZOXn#DCOe!r0MI}diFCrub7ITQb3uh%vRjo!Je-Mh!pgb^xn6hmb-nEB=X%A}-}S0%fNP*D)%BV=)%?i(*qm-oGiR8K z&G*fT=6}oy<~;KY^Fwo{`KdYEoMX;4KQ|YcpO}-(56sDChB?KYWqxKZG#8m)nxYp` zmfcS>grZ%lO|@!c?C=nKT;fUpx=t&@l_SwigS<>tcgnp`>f`F|ddbz3J;X}0hh4p}s#dN*4`YvsyWXqAT`%RV4mZ1) ziDnP;74tPS&g^W)({+NG$YKafHdD-=6eEh4`3l>>Hc`#pnPr%%=0Ni`>i4}%&nK{1 zY!SVcLT~q^H|QRbFlKT@`y6T>}qyZ(v@*$FZ%CI|9#9p%4B7VGF6$T z%%GIXK-$cswE4n()qGX?N?EFWt$d@bRK7I_(f{l8pGN<0nr|wbl+9*$v%9iG*{S@b z>`{JJ_S?j|iVvv#l))`m(^qSSp%w*pB5`BkZVn-bTwnmDfgbE8oU+N8*QkD zdY*Ff1**9_vQE^F#^uV@Zd9whNTaeItS8kxy;&dj5{=JZrn>(X8e_f62C_*M@=W$A z^*Fzvy7ybQntexYWhUD~da$1zq_*)WJI2nj3)FI6W>*wWnxTno6GHaW4%(`Yhv%cBDY-m1hHZq?v8=KFX zP0XfdGqbtb!hFtbX|^(3n~$2IW?3`Le9ZKi<;-xiyjj7lXhxWo%*tjPe3^^nZ%|-w z@;9j*angoh)5mm~rul#wUu(u|F|Z(K5$KTed3zo zn(4}Led?O!n(dn7`ph-gHP1EQ^|@<-YoY54*J9Tq*O#s(uCH9nTuWVFyOz7AyFPSH zbxkoj`TvpYlxeu8xxR6I?800Ib-nG%S$+g$D&$Zumz;(!V)b*?D zH`j63@2)>w7hHdtnyI^fa;e}Mk=GyMs;rhX~)Agfkw`-5< zXV*d3FRsI`Bd%kv6RxwabFTBQKV27HmrTX9nRe6Ry6n1QGE;S(b_p9xsmyUpr7{09 zm~xF$IDpFGn<$B*6mGpn$x~74e6mYip**FnRCY;|}=pI>Uf5w+h33#FNgNdAbtM|Iy{r~{S(so&q(Km#(z#4 z|0U`CGSc;>dGvdirQuS)*Zu<SOlig}xcTbmSZ(`8mbfz|k+G4aK z8KO1zYDcpA6dS1J-bAg+W@=Y*x6bFS_L)3oQlZRw`Of3nu z#?+2Xq&DOl?J%_VH4#Z`WU?#RAx zZI*T3L35(_<#*Q2wQLFCL?y9vUHTM(@59Bi{rbCeV!YoihIfPVyesY#%|4sq`cFkO zd6KGF(=OJ*x5aZU*qyP7fAeR@%|xuCm};E|Al5gFx#V{DRJ_{Z)H<>9JNAyo>()<^ z&555p-JkxB9w;N{tJB;dOVpa*2xujPWn3;Pg&Z8rM+mJwQ;B8nN@pbrAkqhq6D<+ zMelle@>~xOxx{yYz+5@4z(!d1^ZX;PSQf)-vp(b&Cws--rFiBax{PxOQ*oMR(W~No zt=J}$TbxAJ;cljCu6SmhKwT(1#5cp$^X63|4t`F`ir^H053h7cnhuE z7c8frkaZHe*dJKxD*e1&-^E$VF_a%yxi-h0-f(Hxk z4i|@udxC3l3GVKCad$568r)rjySux~s zg6;_E%Ir1FXKwrczXk%pynA;1ZMmdEHiI!q=9^^|?q>KO>lfGBpIb84eJZ}(G@n2G ztNhYN-aolTKcp^sMu-{a8BpH?%&g_~GtUnFNlL0F?lF{DJ8Q|m-0f7;cJhU{U#|B0 zq$Za};A3wfivIjMOsx-shlD9<7niarrP7oqIDt@{UT>A2Xo`pRjFVYFc!e@b1wU9H zm$`Dx#Y(h+m1CvB}4a--w#2d{lR$65-5H#61HTgwjo9;-crFY+ts?1TwFZ zEb{t>j>P4Mb*(FpO?$X@?Y>*O3(DWPN@(U<5i)-9?D`jF$}8cXdrC+c;*_1f6-AAo zhCC8L80zGpAlQk%eIQyFN1kX}mdE;Uyj!lZidd5EL!HK*Sr=FrP99^Ln@9VvBIMRO3wEaMmGWFy@s4kd1&@_w-v+LjVzi?B=DXFsqS+{Noyy&Qw?fP|o zTsqq|o8r$Oy?~ovEkeeEyWBBxrt0Srd;156mSv81Pnmftgg>YSVOn0z;u~KR*oGIG zo17df)Ej785=0))*30^julie!D^^o`sMV>iiuwE}_N5*-1rDyFXsfw`A zl9^{~XrL;S&zPHQ*H?pa9JsV=!$r8Q{s<`d43nGF$6(5WF=Z8P z*hIY8B)sZ?gj2ANl1Ntt#8-RRJF?!xY3qxnGP8W&MO3#`>y9+Ex9aPIG%&X!>v1%` zU(S91g>q?1FDFfwSwvyMRzfcIY*yTh2yPV#Mf(?IVHgrU)o&ITB0p1L${He{r;;q; z5!IleDf}XB)zpys@WZM+QObiHGRCmQTi>=sx`kMmw%0|RQ%XL7ZAj);W`Lu<7^TW+?YxZWzO=Rnq#!>T&O~J>R2D(kD#POeA z!&}jer(!o}(6v|Z^AYR~+m`67hN&yC^}*Vf^as3+3p&=VWK|AUNYR6F>=<5fDt zT-YL!eRKKmJaoR%U&snu;wJaBHO>#}vEm=!?E2~h6C7uO?(18UE z%hgb-MI}qm(EIA!MpUv)a{@~OagQRa0Q!uFugh@DMq6=~%2uqQ6IEmV3!39r2Uxy5OarJ|Y?SfVPr0%F82_J$!VVk5$t~sx^>*YR%V$*FA#Y*Cm~Q zC{vhkacy8N%SF(=#FOcg?OEaZ(^k)^;R$9)bgg`iJcU4?K#xG0OOR)#C;DaJ9fgy* z{^`hR>FIH3WxdIg^OyDC>&EL69+j`)*RPj(cX4;My%P(JTwgCoK96nos06R-^B|!! zZcrb6`cAa*$tj*xX9@l26V=%tQLdsF-V&}7-jB+h7oX>&!spdckpB3KsGKIgo2603 zB@t_Caw_s~i3pmXZb?fx2pSJzhmhnP$`x_YmZZlQ1@Vdj?Cjf$Yy<~J2Z}@C0gS$b zV}Rn&c<@G8;D11ISUglC6fiTe9|{lIhz?v0obt_wl|sBC0^j?zA{z05FM<8ADR4#@ zU@4#g)D<)s%|`}-1#i%Zgc#=W%V03D04fE>2nihR;{mrrPNM891J8mzH~SPX~*{R_^B8QcfNfqldnOaivUU15VOeLUcexWT)?MA%1! zK~7*hbPdcP5(%wO8C(s0iM#I$%8m(%AnYQd5i!^kcn72%%XNp|Q^dX9xE^^a-dac){H;-D1K|5rkw1 zGdJV?XO(gL8R2ju0>3K(L(` zmrnq;1~x+K_wDtUpn|2ltC7f5ubEuJA^?z5)BV7TFTu)xYdIaqGg9X9Z~@5^UV1$}<$lhH6e z)t~HYakIv#zYOOh>OV9^3B!L^%Vcu$Co-=Kdc5nkf1MBgh0i>P({LPM%&Kp%SXP9& ze*-|a=pGLKc}qgaKWl=tH$t%bb^-{TputK0MMB*33%!@cLigu*RL!O(mb3}x!uL^l z<0|f|p#Bu0yyarHsSI)cF9eaxtjTc`1p@xz6A%HPwEoNRWWhLH{=R!rxefu*=ZzJs z>SdWO#z?O%8O-X>2Gpx~L5s$h1cqaN;g16V2NN=@=OHD{?i?}EpO2TpwPyO`|?<}s!KM$f?&tK4Tw6|0I5~ARlqI({3 zw@}lWpitUE?*G{*hpcr!ACSt7vWt+nmz$cVi8kYz2B@wa*H*Zr9pVb$#C(@#(h!)~JW=sB2LahYGJ*^8TlOz5)Ma=O2#2+w6W* zDjw*u>y~Gbg_p(4+MfzRJ&c4ht`R<$Uxgxb?804NU!|JEghC z%zqIzyNXJ|+A=mc9Eme$UeQ86w)&Y$I`FEZR(?FOwY*=PSD zgI>KvM%vQQ99Jq`@%j9GYxcV1TV>{)d7NIZagB3OV|U!b`9Sr0boi>~mS&Ojgn7`* zUEr_WMrKwv{Pw_{n^{Wrve~)%2DgVinm5B@dRr1}0f!0wa^-OyNQBP)tvW%t&7!qH zinnRT<`cJWC+-!hN1kKUG0~AWsJQnpaN&!EW#86ZPwge12hQzA(zVS4$I&u~TbZ7l zcLkpNT}~Li7Bw+FCNcfiMr6DdWAEX8V&w53;n+m<+2YG+>&Rn@YxE%ZYt-KUjXQ^6 zt+MP+t zO@GmG00TV{yup4$H=7mPEz+@Kw8Bs-ts&ALU06sVvxw%rCq9FiZK9zNssC->J9|iJ zRlWPY`q>J|P3sxXyU(I<@jZ<1AKd`Hxb8^*jJJGhhqMJV%g;Y(158hu(K#|byZ0XW z3?2K^7isPSsBMPH{Ro|QkKT+yNZyDyZO8q0S&^Gn?gBwQHY7pctgh5_8(Y#UlNm1s zA`7tW(@MwAN7?u8_cZJ?n$}kItl2nIQucn^hd0fx^j8?iq^%jL+BP>-$t7m>4O{#~ z<@9D!_xD0Yq4v%%DK+rft7RK)S+G|aAK!uFt@|10A>pd_)avSMF7M*1NTQH^OHdXpdq|I5>!@YkA zmQt?3PNz0q%f^z_sqW;J%pR(-Jlt;6_YnD41}8mehbi<`RR-nysH3 zpRlTy8IKk%KXSQ)z{K{gq4MEQ5b1_rh(>K0z4SC>)~VN=*Px!TdMWCsl|)$s`F{=H zovdQ0p9U!IQ6nc%3?qH0(kLz#oG9IGu9UJ4!~b|gaK3_RrAFI$B9DTlc^Y}t6{CdIc0W&)@?SdfJrtzQbDi!trdv;a2r*TQ(jDb1$;~P)4 zXLi_}y}n^d!78^wW%+-kRsGqURd5VQt@wy*Dq@{kJo31ePPebuS$ zKRc3I@M!9#fld!Zo*OpR*MM&lgr?Hbm+(u>c1&{rQrES_WHsXr!5$eN($7Bk`Hbj^ z>i~BkS-C=l%7frQXX+M(b8(p+2NZjej|g=@YeP3u4cOf@BQ9Tn$a8t51=Rg zvx9-&=qe&!c>F@{$mW&DsygICS%_B1Pk!p$)@V!6Y)EYQDv9S`C*`uMvonOYf!jir zx`+<2r=P=~I%80lVAa*Exf?P>Ic804Cn+Fj) zmaJ5HFSeDEgnT^J3^E}38y86@5B2^poH4g5>ltbX1Dcys$~XSqE0-d8vHZ_Vbyu&A z<~l1e$?aF5G4&`fhpX~=k06yN{!QDlk4UAv%=jZ|=9wsWrCF;ul?q#(Dn3JvY5VXv z!EX+K5b`Hek!^prUSvE5cC7iE`wHM@j}lkfH0@MCd~J&T${H)`$cY#2 z#M3lIt{(f}uh@&E%JGFZzvok32&lVoPi$vi*d_Fk537ddkJvWTnP|Hyoq=A;o{!ki z+?5ja&I599)RnP0$yb64e;_@(-Xv!9^>?h3(KFXhq=`v5o4yXa4~;Dh>rEUy!xp?Y zn4Nmx;S&En-LKn<4tisvGuQ6Oh0SsmY=5`6KiB1J))k+aL7?h(BdUS?4ez+I9<>of zlcKA8n_ASC{_(<;JGO|-_)SVSvbg>!$?P>s05jJq^j0>DxcX?|JP5xBch2iz_x$LA zPq|_DhIy<`x&J^YjltvXiT6H%yZ$+N9@9-mMHr1RWyR3(Gy+>YDQljU<+tkjY~4y= zCBg$*@$^e5GOc#uT*aEAi(;rTw1^x93h0x4-(&mVJB7Z~=&u&KCA-SU4tnB;k!~5g z@>!ZVL(L7z@3ywL<7jPs_6_arx^al5rm~*}xLNBgJ35({@1q5ndb-Rcvptea93WVs z_gMLLbZ_9jn}@GXN`JH)i%SasLiLFc2rFAk0Ixh;vr;G%p$Iw{6cj>SvIFkQ z_VFgB0jsAX&0)ZR+|Op+N4{7#K)N{^aUmZ@kG&zWs=@f z?oxl2k^AV6i&f?@(Z$hR$!hb}u*$0oV+!@>>VpeaYyA#ZTb#Xwz$)|CynWkPe;A6W z)!EMUU56`vu>JASGTr&dO@aTQ8hvrT7>qj}~26Rp2jr`c29a>-tMZx6d&S;VDRS0CGVc4v#@4r^{xp?er1Z6vW9 z?-KFo3cNNREJmk;UE}qK{3e7}Z405!aB-Q!2sDpox%XRI`I3Jb=52E|{nw#(92Nmf z8e^LOP``Poo5$KlOU!+b-dVAkD<|L=btNDsSZDqLQC5h|r1`TNyREDy6LtRlv4J+y0CF8}pejS~)U`RScP`CO$)}K9<%Y3gX@sEVX`9f9AZSAz+(QKtf zhFBUgF|!S5Bg@+f#BZy+$D3p>UqyVzX6Jce@1NLiuugn;P=_Clqs{bqn2!+IjMZ?u z7T@$T)`=@o{`@jqxIb2N{*%{5GwE4e6|brNMtR_~b_ICN+~N`!KRp8zqv7_p&nlBg z&|@vH5>n>saE9r4pH;TJar|tc>4AHpdWk(D)(8?e}R+w&;;Ug>FvQ*rwmjFcArTInOG6lUx(E7Tr7qB~R!Xx?1a`nVuc_wO}w9%~BGmY4t z%L_{IYz3|w+m!I0>g}#3+G^ITFlhxhtmjYrxwt2&`k$!Ll`hle0Glbkvy+-icNK#s z^S|XLnqKeYn?qK4+Z_Dd<`37)^U<`;TTT{SQ<_s6N@}GzVblaVZ*sG?^B25*tQVf> zha^@TrW2j$nZD;nRC38jcwCd$MrsOLG)iZwLhP>1U53o7Ja;Z-YH@qfeGXxfM=8k| zI7w5p%KXocXT@K>5~-xK=Q607;E1i3-<1KHwJ*!a-9sx*&5AMr2sQ z9!xY+JK;FqC}oU1^90*)-yTOfM#2(@q_*;1bs10v%Y|EO6xMpaE33jvWZt)Wtsf1P z%CB0F(?1S&MGJ%wZ=MKCP>U9hpBDKfbPex6+$GPI*>(ZmlgFzZ_Uq#+&gxj|IsVMo4S5WsCM0{C>GP zZD~L2C>FZMyB5?=*Q(t1#4WH+<6}QvVCz`g<{i(%d@s1PTCHmr%i?LRaTzLSd>8otD~hb9Q`gGxl;hM1<{KnflK_uGS4O z@*C@02KT7ox(=+KX$;1F~|M z>F@xA=YHX0wD#Khn10}n-#LGg)x_<1qP^Wb_`F!ZW7fga?sn;2Z$zLALY`RmXlXhX zv4yC!{;SgChF7`Wz0ulQamAEMEUr$56^6h^q)f~m0L(rMT23I?97 z^(PFu8O?Y7>PgnBoKvkVO?SE_M6~gG)CC^JcMNsQ)^Z`J9g__j#q<7C{Pk@*&WYg{ zD#V;W*O6lzM41KO|r>-a-HdT=7hv10!qu3m$PV69!J(e4zV z7CKCc=4`d8lm(MA%X?QTGJLvWRK}XeG=`PT^Jc?9HK`zq>oN-s@sykWbztCGjix16 zXh3xTawzyge%t$m%iG`Wr91YOxl)$L{ivt6xFOg>oCCJ6D>jl>)6qc% z#UMw}a*0D8%VF*AZ8nEMN$kW|hpAF>gan=CW|L4|;-XcrM~OBD4!*^!#V*2*jQNc? zZr0Lvr&t0-@`k>MGtD|ZuE>LfN{m~xA4<9n9*{0lpMe9 zjwdTqaY`@ryY~AXs3+THmaf|RVEt41?$u6+?ME1rjXswMdY=rhQ5kHvcq!D&2Af3l zJGELp_qad9)wOy9&u59t!1jJLbb7*G;5{j5cYL*^e!4URtVELJt*aekZaV z4X)NBVX@>Muj4P=dJDS$))~((^cQHOWulr~*qS5f+Lz^h;i+W2sEqlYXK3ypw_IKW zxX*sp$IlM=-+8C2O`6>ROL6v~EHy!_56J{RTl63&|1 zn3`Zl=vw4$r*Y#rl{#&dzGqB+FkU{_NjH?IU;J&D*Ff7&%-a%Uf7LrF zjO{j2bPZZ8w1_Uf`?o{dFY=3U36Wat?E0v^or|r+vc?k1*WH9SWa(fT@1y;d zw#g{frOz~+FZUXZ_odIB!&iS|DxE#goZNHbz2n`R`nz|}R2XmYz4i%B_E~fECu}ZO zXE?VDn-gxkqxiOIT4jrh{59}iNf7}-{D-^%`8X7un7Yhulw zML(wfw$aI7Uc1V;2EF8*&&giD-(F;q9($BFfGX}lI|*{^!OqEzMn4}zv$X%tkpG#F z83By%PFVhWDssBm7G%w|)-}2=CTGQnrB(aTc-yty3{MM6%5pl>n+-2U98Pz4S?njJ zAAWb)yGPh|rl`&_c~2#DB72K|R74|meJ;jC?iS#keBQ+98^|PZa?SglrLwVgV11b- z6G5j<&F*-6FwsK%>UIF;#%zq|w|yF^XVuiESv8X_-Z$~o?Ak4RdIYO62J@vpxwXIx z);xQizAiQ89+UZJ60`80F1R0}ibqv21t6?5D)r4%T3nod{p#1n`HqXyUc5(3tXOR@ zw~@v`8#3bkSL3ZE)!gmRd{WzMunK_3sd{5Ukf9Y{utXY!+)+;aST!^M{#35JGM#g( z?q$zp?pg^yYe4MzX0*A7X0Z#Jamijd*g7VJ555h*s(Tz&UiK{M*$|>P=WZ0Nt*Y^~ z9?KFRUzzKfE^#;tcGMz>ep9sBXof&|0vgX0?j4TZ%r^aDlyiAHmc8`CCpaQ3*|v_c zm30@eRGErLOANdVP=B^k9T}`v5;N$O*!%7nPy~;<@$8LJ{>y5!Ep`s2l5aTTzAhzY z&njMXh;#Q-EWa-G?qyIOm^t${*q%;`{T!uvy;wKxDdI7lv9h?};{2xP-q+&^^PAY! zMtk;s+1@ird!%DUYOy8YG8<-pC#=@{?i<%31_2dEaLlak5U!dMKWxw-d8@Wfnb@@H zJOSjm4Qk~U!PWlLZTn)DBixk$&z)Y7ry*(W=}I3m_BCgRj?-T0X8UznwA$Mt?k2VqDU1MNZDW&f-@ciwhoZj| zU$e@sI0S)`orm11d@+yj=ZQ<-jdTc%8L!QLL*{!)f^KnMwsdc436yOrR#IPg9-3?l zOTJ#Up=znbTaI@a>+qBdD%npobkLz8?-95bzoTkbzS}HM3v91SUDjF$^R~XYSpQxM z$bA1Lb?Y4M_D5dD-RMz#s+7sqy58VylRF%D@uj3PBauDF;MW->*xqaSt@_ZI}PUJ0a89Wihu~)l&N+)INs_LM?()lKjw9**>q0CZ=v!zb2i)N9`RfCC^UYr5jv}tSy|7um^`W}k z@b=za%rSKFahH}ro^fBxu<3cMdYUt}0V^ZnwI(k?v0}5)p-?Skx8OjN%0(^}Iu z*zpu>dO99u@I2fOJ0@?eXxTPk<Xw!eAvRe<7IeAH?DFj}$$Wg#9=ff6yuV~@VDx(*-EdX#uC``rcd>u}ZYrI6BbUHY(QsyUcJ*R*wZ7CU)V#Xn zV_ZUlZ)n11ahqVNZ!DZ(*}I9UwYjO@S9shiAEdAGUw@MFq|rqL@gQg*Xeqz~DY&Z! z_4N$4Z2ef^3`uG}BWnoN{FZb}0UACzwGeK_f~Mus#b11~PFNF2k_hvv6{- zSfVrC7+3J`zMtD14bA3sxR(6RUtE8=n{02al#yS(SX`rJ6_BCUaOdjedaJ4YQ|V#N-uF9FD6^r^@wm@~z*<&$Xo@oo6%Sl9fN`-}AheQ1HD; z@jCNu79NJPFPnt6G5!17=#aQ;;wmavU3p)!_RsTg5ruj2$*GK9OAOIbtGSOav}wNl zlpgyE_Xy?Q3C;gLB<`kJ_!yLJ-d|^>o_WpMDEd`kJN<5Fp*cD!QL@y_B|@#sSG+08 z>ap+Q;k|&WqFle2hox_TPL3i3AxQpW?guS*Tt?-Ve$#rp%hcUKNBh&L`fJm2N{9ER zzYqU$fCiD5OhjqqLm^AwYiNg1#Mby=2&{0hj_04&Dm}TElCyVJX&vb^eIC7)%lhGW zQO(u!Gu4wM?F?c(erFtRy{^~VS4H9~_x-s0tN9W`$31ONruCPT{ee;kmfBYQ z%uy4bS?!6kS3h59DBXI83{)gzYa@FHJ7aw-)Q^^pfjKH0D+v?He;RyzjN)dN4#swj z;+Faj#-hfCHb%ybGRD>>4yGjROxywj|8*mhWT;mBSVbDsmq6XVk>x(@uh9dcSftQW z`E*Zt1)fkaa$z%2c7tR#x*LBR^T^;GcDPemiA`gV>I83B^J8uFO~<{I<|}Ocy-_A6 zT^uhPE7W$GE%kTGeqJN}}?av!t-79+&&R7@5o?mYoA~D{o)%EmwD-F$0K26?o z-rm*nOgz1O8(4okdzOOym{2{gye9Un7+}0zEj6ld%UXJDP18BA-Pi4lor$c;|FguP-2;J(nkA(Mi*|dXhN)66BlA(;rw4*2rL`c!-NueFA$h`n#(oD0~dr6NVgX(l|}pO7?jzRzACfd*L%CO6IriZ#WH~_@gpsn&9@WmRdZPheRRpXLH<} zGgDtwRDIvj|H!p=Z;k~LMT5MnMef+TH|HEACRZ`G8RaAaNJSR5T!YzYn1W*g*{n|H z83zL8RQhk<=Gn>On*;@W7B+07`pF3J6;%UOP-n$T)FjWc+Ld4R&Vy>7$bxd(B&Fx%&_MBs87@`zwipi0YZ8vl2OV*fue6f+k$6F0|y%{*oj zc6JVKE;dFLS6gF7<^MKy|EC#U(ofnDJ+%5}1^DdG*G}7TrCLg7D~3TD393mD)g(IH zp&Co62FsyDt@$>eNGnSj?6zbg5&*Oxbfpo2Xs{j#Y8ocs%cl!r7}Df`?+$dY6Dx}` z#>y}>=SlbXr)625$5IKa2OdQ6x-cqUT-+d;tay&vERNsdz?9~y!+nxfb&wsK1t$sh6wq8Ja z9SV9lEPYl-)u_X@6y|y*3%KH_(5}_r{)?OeIE@3pSj?kUye=k<5CctlZ#Ylw73Zgx zke64Y&wmw_zI^502?=n2uRAsCuJpc}9%z5rI*{u_yfO&+@g>;Q$4-eY=z???RoCmX z4>b&}HMgAhHn)1%C>? zc>e|Fi%86V-zE7K$na2c+=@5$-!6Py`(Lrn`h7g*|1tJIy6>ydx`7y^=>KncD}`yq z!GorN5+u1GevF_fLaKsdirWZIAnJgp5GMQne~cH2?gxJA|2*f75&r7F&IPFP!l>&B z%}m$tnlBpi47=lkx79QVi}9$bf)7?wBu$OtQ53`%wfN^&hra4_m)8khZyI$eEA|Gj5)dfyvM^`n)@%C6b2 zLEp9te>sV(0He?J3e_Ei`g?f&2;^!YMaq2HP2z)ke6}`vlZp0^P%-&t6#qoi8-thK zDYqen^e2B#34G~`P3jkcS%!+f<+Az((QM+Ao?u88^tx=9cbI}5Uf>Jq20qdw<|Hb7 zAi~mdaUqG@bs1zs5PA);fO-h?Nb7(F6CZk$?%LmvdOTmQUF)Xmup72JlE{Gf?Pr~> z(D{oi|EhLIE${F8#<6r}PmSZ5#Jz(4pr_`gR7uC#_~EdtnyJ{WTm@@6C-Kg#J!!|~ zF@!d`Jf$ByEX5?7+z~Yd9+94zKeHJr0S~EZ_L3{}Xn@x^j3)ykNpn5{`O#^r-`C%= zSYf!6(!s34_TrRw25p5Hs(VK6_#T-Hn{HVqSod|#z8+p}mCKmCWW&>9zc9X#8hQFZVJeYl>ND`$q8zAE3)|sDZ8FU{b678uSy9%7b*W|kj*L6cu{w5r!iYR zn!RKNDKWwJE9xt3b;DgnkXzFHGJQb~Pjy9d9Rnri@f97FmH56}P%SY&kBv$+&Mo{b zY`gEcG z0{m8)E26brL$s?MVrh976GFE<1sL&4g4!QC#zs!6Gs5|}7qdMtvxwii9OWa>Zs;{y zBW&L{z?JQpM^;YibV_K#`GRZnL7S~#cFxcJ<>;u)P0zyuJuD1=>(Y-w0f<--MtGZh z$u13;?X^*E>7$au0`ZTeTKG^!Ja6lK^BHvA=U0ntU&c182I@P$1S)cxNsMYB`eT@W zJ;MKqVSnF*py$PK>Cn_#Ey-C_xjfIlAS*sp7;W4RAl}CoRHTFVhI*U&sX>}Ke#=_>5C|8n@(jimeeICY336RX_9$+e}8 z%|jwi>yiWTmh7RV?IxLQiusyuV}(B^iywbz_O&c)rgQrT_7-Zy{N32;P6)#mF$%?w z5m(wG#Tptd?i5#%xC~jI>3y$4THy%i!SP+!x3D_Ln09pS@a^TLO$)2@LktOC0qNhw zxOwx^W^_TVwkcwNd6NAjg>VVfnAg7dQ{|3PX<6$hdFE@2h`{!Q+{YE4;P>Z}%w#MR zrs193_2IyK>P(HNpXhTdxtrY2c4=6_+3#DN+`Han_UF9)yUYj=3L=lmsYmr38P?ZE zdDK7%@*vbsV5q*VZEk33h@IfbRa@a{!715NIITjcQVeFH2u7klNtw7>wg{YP zJ2qK)L242&1-QGFzB~*!if%h;5?Q%vr>PaZ%N4u7xm4%!?8#6y3<8g1kD@C7SY!`{rz|g~4O6vON_d)H(3fx<7I5QUUTU-xMAD#B9f)u5T1p;Pm!2w>a$}cGigmyn^|0(?TIfCaeEZn>r6=rAm_cS9U%uof zA}V^~?25>V@bIo%BNgr7FpS2pstNE`46Y2#MvF}|;KjXjWR@t~W(aJYWO?XxI)Y?s5yNt7evP_PsIInI2kKv1gZF0+}>91Ywc zVqkM{L_5sChfWRkrwOurpN5<$hSV)TYLB0ssp zU3=0n{$%kL4`QOteb1`+#xgO;*xiC;rFNlX=r1*A)=MtTPHKFx9t{EoemL-y#Vk5d zb+AbqlA0Uqza~{;Z}pXApQ%%KdGTbZ%<6u|p)d@vy(M}rteU!F=Rbgc>2ovtmADWF zo0de}Xra0x#xe1D)!KwRD?u#_ISI17gOT|RM5&$~!ZuXUkVVZ?4=|>Z=D}G}ArW_F# z?`7Tj_dM*mBTKg;I*I7dR!yh()Kf+t{(Ij*ex5A(L_yzhU19snA4S~zfmxjQ{Dq^9 zKdRl$olLITWK$Un;~et3jfH4h$}4NTx6OL&Fz$&ySx3iInnDDp@|w{HB{yg>G*O+( zhfaM)Rm}8<#5b<`h@t4nqV@7i)S-+7JNWolsC~VT%b!wmo}lPjaD*>xtS09LCAAJH zdi{y8MmU7+#ZJLXTBrg)NBqHbM;KU$1vcoentY<)Zr0jKOuLzkzFozm`U1`ZS4A*> z@)e>r{HP&!?aeuJ7D9=i6gF5@Kp!i*h>LIlfal7PKHm?&3@G(_TllfnYL#JYaFo){ z_1A_WIwe!AM!~Q84yUDLm8o>Kt#@ZkF0|B70MD$ z3*igNIjrsJ2dfS_n)4HMAfbF9^y*l^baGWVfH%eV2c{E~J}+hi^ClLhrExb7W%_79 zTyj+kAT7Bn20)Np6$P+JsR{y=rc{Li6e-J$x{)a9OuAVp>5RKEC{bASeE{JpRUv>^ znFGUaVM;nZVMfe7R(&E&x8%vO00zoYFcg498PA#%0f5L*>kBhu zh9!UbEli4ulnkB-;G+yk29E@MqcmbA9SoqOG-4t32OP?LV(OX*V559U1`oqAV)Dt@ zvXTY@hGZlS{K5fplw8SrCVr6sHOimKdcXZb0pgTjle10yq5v9{rpejA{lWksO6p`1 z6Tb+666GfuFq5wd%mCad92V{c7iNF5Z9G7ic~hFwqbpz{n1ZL-*AA4j7tEZL$A9>#w7%R#J6|+u|>tK_}#54^EVct&a`Pqd7|HKEb})Wki@iUK&jOm zfSh6rjX7@8?JC2S1Q3zo3I>?4ZqiYv4+XF$cdihP{(nbnCSdfv>;GSzc8&gbEO(9m zzrgW1lxa9aAXaG?AKvePyGvICf5%uhp1$&)VfD${sx|kUf z1e99qr^HRKBVHV@1b`TT#Oi98Ai^5b&=3!hYhBG3h?WLJuo4G^1VRT&iD#jPOqamM zztoMhfdoLsb>@r|X!QF9@xnAIN*ENl6#F6e2w?wM&t7iKLGd9FCVl)U+dt7E$su6n zA%jjGDVD4*fJxNFigh407NU}W7cluvE5-q z7->9(Dm~>4vlO4=r>Nouh^i+Lo}NS6U=M8A?$klViciRGcP9AH;3;$v@uY=!?u~%# z3pwPTx=s2Jjcw%I?1T{CJ+1#OcuF&VEq{J3`-V65fd?{A{hByc$~IzSdV&q=AgUgN zcXRFKKs=xTF;4aBD!TbjUB_qU{mO*gpvdyc9GZdT72RTgKT<%bOt-e~qz+VZq5QFB-1 zp!q3_d4=ZGDb@IjeE5p&>JggL90$BMCz|qI3=r;_AO$W^rIaPr!O*J`=j{Mco<0u63)f5qOTKB-AafSub zl%RtJYMNfg(foIZjptBW0+#B?hgas+IrY|jHAOAAWFKn^Q~Kc20MSy@7w#r1YJfBw z0&s})*?{&81$7}xf)D`^G7Wup#ul+6~d z^c=zsGs9s(nZ;z^D^qYEfqreBeMr9dE!&Ju`adrG`vTb(Ig(x3JU29we2?(MgZFBE zjwD74VP+$fondC9z7qJFQ;SinDwp6hcfUmyPX62t$ss86Q+f>Xm5F2lLCAwBvt!Xk za1|~?auAC`bdZ&Qd}B6(#hy%mBuAL4ZbgUvl|~9`s&o1L8ej<$()fh~k?zs-HOP^x`^gAmlw(w2 zlxvg^k5cE8NqIzVMq@@@Y$k_2R-7M-1p1GsE2S!Db5pRz6!t21$150VH4e%$6=<`@BQ9!kgKAh}l07Za9W?AiQXHminwi>HI5mc-M$K_riOi zkezwWrQCSK-hc;Z&BLuuKxe}9!Oo+JEKR4Xo)rSUFm`nPUIKmxKK~iByEjZVK9gXV zUJcGZ`Or3IghGQQ+eYhp_V+=Ax-Xq?I#;fAUN2qSXqRaF=gFyuWs94?4&eGU|GBgN z70dzi6`>dESw(Zjxu~JpM%~JOySs!pAl$xs3d3xcrBP+hM*?~rEy!`qV&Ud zVAG6e`<2Prbv$Rw#K!B{^rO+o86h9-~v5ufY1RK~rnNiM|-Ud-_Lm^-HqkqP-sak%zV3B+#Ivgs%C%=k~dLVEEr<@Li=dgbhYw zT%+iDd*nq!3z0t2J=Qj_KR)HnqMHpAZ?k;f4>2ip42Vea_bRQq@! zq%8ya?#^kP@$@jgv9VBj9`k5(W)-5HVV`r5KDC&U;cnvW6yM-HzO=l?UF#sek0P0+ zv$Vu*Wn-~FGqpFK*fy3S3yI7?%|?vRm%sXc={A*!CpAgE(_Qac5ppa$ud9Wz6V^mz zcPXAReY(Tq_*si{+IOBy&Qa@N!|~4KRI4{LWwof5-@?7;&LpiDtJ-*vgRFGzgP|N} zO7{3>F_KJuUU{4mqvf>W2_}*UdG6N=PQ~T~jtAvdHKJj_EwJ_5)wfO@XL4^rN_h;V zPut;>Cwl)EYhM*qM-*&}1V|u2Ah=7AgXiGx?(Xg!++BmaySuwP1UG&nIwWU0*-@n*L4Sln zC>xSGa!Y3L7I8Z%I?`BKyBAf3@)3j^2Wr_t^IUdlY(5|0Qi9 zYOrd)P9Yyb(|n_pJmX0eTdo&=-~Och1)zuxL>55YN6`d|zV_ zh9Qlh{e5Zp$cdn1kiJ5^`7-$;$G~4faY3*W!NhzQgKB}0@rBldTY^yWh0%jefe`V9 z%z-LtKmWGedK`RBAtxb_ptE4PU|dnJBzu1P4namk z62Ra=aX~%7Z0Yw1`ocjXLtMbE!L?x8B5zsuP;SxnWcmj9Li@h^qVzBj!7jnIK-+R| zvGnNlRQhH^TSKmWZ~1n`*c0iS4do5>^m_}o=ic`ef)`2)p#|2Kd5fwi(U%$`8TtW& z4q6Mo<%{j_Es~x)Q*f_x~0CZ!gXb)n2g{c!CtEbz- z_XZhx#_?y6j{9Pf#mWq3#n<^?RN~0fhQ=M@9gdN^PNW@Z&BvOXosZ>?^^W?E^N!oU zgrSeFaO*-001`8O3_QiwrbFcWJ1dM9{$Ld68d4a%-zCh7dDvbm+q!$H0*1NwbL4fWhZY-~7!g2=n=TEyy)OOOs-dH8+0C3Ap0l zl&3hw(!l-U9hXvvSY?U6O9Abiqo{A>oI5KN`dfNAQbyFYrzA6QLgdiX^ep3>`PaC%B!WG3Dg4;G5OCZ&!R5;(&)cuppbI z^zT6nf%wFFO#Fhtkl$S0`xj}gFJ3t}wFJ{k1ht<-3KXFIc7;@)s?35^&|j=r8L4-c zXZt%+H9y`Umpo;X_b*0N+VrFGv(@Rt9syXNfHsu(?}Ixg@&;0h)=B#Ah++cNS+h6HBb1PMEv5%7cRZT8_#?m_lRE}Ce68u5`sh5#Q@KQMj~*7yxWJ+rg&b;YAg@&*Sw^V6$h{%Fg5mEak)Sy{Bb_jzY|}b z*Mph1iq+)Vd0eLybm=E!xgmJI26Jr<45JJ>p=ADD%bLX15V4!^vsixK z%2p+OU!OJ=5cP5StA@ehFIe4u-`;#j$Zbf8fZlb#qxt;r6S-X4N)0a@?&%^N?t1(z zzab|*DvCD!PgR4H7d~;RJYd4qfAWw2NE{i`Lie@ z&5=IDi2e`M(_o=f6ffQR6)KtdWK)E|gjVNv2gkS0cXuxQC;C?>yM>Uq5%0#~%X3wf zgYU5k4W5k}=D}wYolzb9B88~-fb$q(H)OnysV6}cI3YO=yY^A***|B`tqLw8?d`Km z0LMIwuOgbMxivV!I^VmB!= z1mM4Z{~4+ebO&T0Vj5rpNi+)hTbIvk+@$TTD`HgDcenBefIT3`j06_%T>l% z9Zc;|*}t|Jm1SIdY9$VVrJ44PQa_9=T8)mAlSdMOIwXn``XNGLsLFXrTK z02w$}AtjEM0k%1cFxC=fj4}i%%rIsxIe=RzoFGpac{(rHY+A5EEGs<%OYSceM?}hS zI^@3!1^v3g;1L``a*$`re@-@_;dJ+(1YdL6T3d1C`OrsuXx?cSDJSqS4suRN=W|~5 zv>xeUQ-#pHV~o(mCSoa#0?`$ul_H|e<7FJedo%h%!wpD;>k9P`kBSm&@$Yklin-^1 zOZ!G3r9PSPe>skF|8Tt5AJqb#H7LIZ4ky%U6z=TE%07+`XTX06ldNgk zA!A96e!Qt3b@l2wy-)vFOkX=p6FQTSlyEj{Z{MMn*Hm20=INX-F=2D~uw9s_QmoP^ zIUh#qqfoNnkt1Dq6C(U&k*c%(@K-|j*8}V6Yd=z@y z%~V{NTC0Dwy?$RCF;Sm~soStOc40Z;wJ~eD@XO;Fw{rxsn774n!A%m}{legL1%HF! zJi|lVK#%JbyMV|!E1Iis5;(N?n>g@xmXGh`Ns2M%BQ>EZR^y=G*f=-lqY`RHO|j?M z9IehAFI$)yT4Ac2Y2%>f8C|u`MWLP6z<>J-N*b}EcyyQKOAod&RqB_Wx>}wNC0MG% zeIngJn5t(%y8P+f_-Jn0Sx#x#U>%kR#?%es2g^4E{U7csVJb0M_`^+m z1*@Xhu0pY8-vAEw3jGqOj5%|8et6|!vPj>5(a#Q<;?xTMP3TnC`_gYrLme>D$3^-+ zpP7=mT=}z7#k0yQdA?5L4}a>2p@VA#(E3a(lpfl$)1RKmOLXGf~IB%V1-5&lO(wTx=HTdBaP&Fe`<=-iMZ_g^i1L}b{}y< zr+r4cc*HLrM~KwsYGqVNZL=NRnmZ7N<;T+8r!zNxZ^d{~3?VV|y*son*rS2Fx#t&5 z;qpZxY<8pqE4ZeQTkKQNj)C?ueB!_$`5OmEb2sMg!ivutg-kEgF|Z_E zTGHj{wa5Ls#4=n`$HH(HLYWB~n-maBNheqNow=N64=Wd7B*(#yydh zKJHqyIN&w28hC`&-j71koh%JCuHF z)^ez1U766N;eNOB(fcwt_cEZphLZ&AVEyrg(v{|;kl~OW;?X6ldRm1ze?t{WTM>lF zDdqgmuzc0;o=G5!5WOQys*#XYC8!n|qU$?4LV&Ce9@(f0tq;3_R4DRHh!-7$UCpPg zKz2Y!4*U(jYs=P54npe}@T5a8E|Q4boXq6dXTEp*20w6aPrr;S*m;U^7nk|x>^;~Y z!5u6x_l+82w-LgVK5{guzezN|pi8M%0EAX(Q2ym)Oxl|_jZ>L>zmc+Zksu8BIuOBq z1PKTJA*|eYsgWus#BaQ35b%{R15# zegKY&SD!w$!knResK}X!k6G%_^P5lQSd0;QabQvT0WFV>wMcJXIOfC}er)Qx^O8Oq z>%3KU^#zrkTAzbOhaySjG1Aqzo<;dSg>*Kf*W*=QRfLsJcon`Mfzz*f&h-KV^8=tTw|KY)(;MGWDRP@80;MGBhIHw< zEZVBTGx@jY-j}UNe!8y`++W!P^hYR_Ij@3+#tpQ{fZE?t^K7bFU#~Oc!hfv^qP}H( zk^Ci|y9lLte)(v=Xu)2&^ImXl>+D&}+J&@GX^N1ub+1=V8M92?))%WJ{;)eeZh-GM z1)A**rJiqlVgY!KB4*>Phk7}95Y@--A-h8q&^R!g|x z;ftGVtrB3HeJky?yuzbbM4wd0RCl<2xfyk0rO~M^ravV228$~ms21AeC0|%^yUA1y zn8%vqi$sbStFWIHl~v|zvTk&i=X&=uYZuMZfiqdu1Rgrd2kCZoq0UWn0cL7<*}Wp} z;fdFZ0YGmHB0G7v(We3!*~9vfanxUeXiNf8Ihk5Waggw+Fw`-^>RNk=U)>m}FTYDU zH6EEJ`LWSRVItPD6^63%CAf{{+_hcg4L}v(W%QYg`=P-|a%XB-*onvj7mtIgm#q-E zUBEjgLMAmD1M9bieFMof!zII~A~I7gym?<`5RYiG zFc5wtY?=MQ{QSY08hV0@6o#ERZODGk1hS3mE0O3DHdnYXktvG-TaB1wi0a6<+Qb!E zE*N!==_=|ZRa_U5m6O@Bs2fTDrbL5pWp~jSnNHCXzJo4f$5(>B2$JY(Z{g7%0w+oY zF2HbHM}4MhlC|O|<~s;w8j%FhQ&{6T>`)Ddo>Ed+b2#j@jS`p1uQIWVdXA5zkv1K8 z);RV9?L_d|r4r<^nj?deLwU%RyneyES)rsKh|Mj~kiv-M0Xd@mW!|?>p8Nz*UFx=# zP&R#1WR6Y}P~pZ$Rj8XKsveCFR&d}*MmQZs_^V=V;F{C1grD1uZhxO_(3;m3lBMqZ z3&e~`%FKh%6?{&Eurn6oP5fsE!N6fM`4WBxHfi&$-^P~$feOTC=Sre+hh)_`{Ek1T zr^QM{B(qSFCyt;3FzrAV%;Hx9!oAC8A`a3H!hyx?)v2J18Q5qvW?XU1_t`^P1;i&4 zm|(qK@{h?}x!hR1rs2*6s3%Mkcl#*|x%#eD#p`il?UlT(STp(hO-gIBkK?-MApjCr z8x(h~S;53E+EHdf;kWb$X^VRThuSXj{apoFoQM)MpF;|h~Fy56T_1z?x@{S*I= zf3H&FCqLeo^Y#I06`wd^^2B)ltCPmjCn7w>=4m|^Go~~fT>OaT^Pt-6>PWQUlUC8z zkSzgrhV03oLlPzc3SLqH9zuVw7S6be+skWLt)y`7RyKOpIPm@fm9__PAyKPrZP+H{ z^1>*Z-QdThs0}^|l8T=z@Xq7SBTtTP(cTVi|HR=l4qWa=nZ3R8b6l;Mrj?0m0cCS< z*BhOaAEo$Y)w8VUFPoHeX-f$8!0Fv%l4&zWJEKOvQ$6!VpKmviLb&R;1vPBZ++z~^ zWJaPq3Im3bscNyEP5C^PbQbSyns#R5YK-B1Yh4_ZNO*6u5wLhEKD;>CxvY`^?+@aWh8;Y`F~1Np(f0>cQO{O~piZBJnKyxJj{&yR(R* zh{jT-Vlmp684deZmpPnocu^pM=iF25X4rgWb?+bwQ%mgfiILqW*`@5cnS5A9NnQDJ!PR~2kbTaF%l=>7`pEirkyyP)(Rx0BQJN~g^T?JL< z%AS(+aQ;AOtP{hW$u7#Rrj?BfL}6ew*<4j}_`ab1nBb zf@ZmP&S|tGMw6sv;e-_(3BllS0GH^@+mG#TVeU-L(OxCjmW^s^Bdj%Ga~oBSHfd+a zkibvLBo_(tk<{kiA24xN@jX4K*pSYw@uESw@6|hd>xC`;(hcP!nzv5FkH~_@ zSH(;*QCOXGB?1ZE_n&W?c3!ub;xY-)5cM~u1>ugkg>m#+%jX_khPz+*znoGRziUaYb&FreW+Gh zBR{?8TE1)cm7$XFK}aVbj|<0f;6{OB>tccKww$)U#?KB3k;C(C(i$UV!k44zWTeFZfzXh%0mGurz#3Cc8o*;)X-_AbD|biYBvPj*@*bVIg}+ds_Uw?tVC0^SBh>< zd`ZL@q!C#-*#v3+-C*@ zcSU~*#|an-9G9dvT=N?p;PgHe_q3z3*Ek8WZnB0|_V@MeTI(R6;>EZVm8H|ifNkN` zT;K4|#EZ!fF6w_)6qMW~c(`OZFCL)wwTrMOM-)hNwi>{nMc_lNdq*P!$6M| z<3BNci=qiIY%T@DAInL__eT9nTtm-fCi;ig!HMF9kkd~|PshHRY^5_hwd&j$^O)r6 zDqDqAFcWblh|Z*~=BE&HBf=!i^1<5TfBA(*lRvm@GXl<{isTztoT73kXrf>yl}fF= zDf61b(&8zjNgLVnVs+HXGA^!RF}YXEOaFAkZWqToQw=aE1}x zU*OtZS6$hgPB7x+KEtix6^CxwERvmMA)DP)^t;3C`Ct$Zo&1_k|8)tg8|H2{fUe*L zu%|(i)-(~(v=Y(O6rrJ$&NWKgjbJXVD5ZE-n7>3I-{gD{Ra^vLd3&C}^qb}`-VLyE zJReZUCvDWj4?}!a6lt6{ae*e;95%o_Ju$a-U83g4XW?7AN-v{*4Qne`v9r_=QztIf zEkw@hNx{XMbR+%JP;m8@6n5WSWxmAFjOMAFm13*xx{wyMGH_qIjIUW_rF@E}_uyF8 z*a+5~$rQE{Mh`E-pe%$>{S!Y-i=J6t<}q`Lx^9kkS`lqT_K+6g4UuB#8@{Nbc@{K{2&U3g*_mAthqjOH@cTXZloB4m>!;^v0 zB)hJ|>!dEt$fIx=kntNw_r9EfwEQC!Ut6`Jlup;qTs1>~a1E0B#9f@+)%x6V(8zuw zWFp2ut`}ihT4b|Bs-Kdjp81Xg%UAHs3`V;JY!tzV0T^+x9AVSt_1v)s+<6D^|Bw}a zyH_X_adzyIs*ICB$6;R}FvX8>&R;i7=%Qbw`r{t2NKd+{j-3e$^GGo*we;ONK;q>G z<4;{HMXU@X+KS*TZG?OU8E&8fwaNzeO)mm^>?gCNF`_vCyYXxN!D%?T`nR13LK_ky zLyQqO26**C;UR3eofw~bC}RrmO@Y$v(I$Rq)k#6MD$K$)*KJma%NK)tP9kw#v}$H{uA(52d}V_`S^O4J92H z`*s;AB&yWPFu`s=sg~gKyeu{AtA> zYK9;oq?v<1P3t#M73rhy&_O%pPpc+i>F2zIn|^7GphFEk+j3Goop;mNTt((7Dl(gQ z^U~WkcUL5g{)hdw%9Jim#oMUdR&Ow{&!CC7tGl)M z>v#(?LDm(b=v`+Tx@0+$5zW^s8gM8Vf274|JWMun5q&qM0de9$+ zBmpl+rqQqB_*uLD$<*{3d`Z(F;gB3t*=?rw2G!-8LEYtaiY$sIuiaui`8a43C^E`?lWz*5#VO5Uvd}5Hq+(jNKe0zJRfR)WD2xckACcqA?>3uwP*0sm zi@dcStj0!+ge{gQ4qP@Cw_9SGT&zA$J&>*(ZfaM^+{1;BT+W#;{-s%`2{+mYjXRP0 z8t*tI&N{~0-O}e$kFN95h)xiywv76Tm-M)5W6;T|HN<+3;*bW+K zRi5T{@N*~!;|82ZpJf zHg71#DnuULvWWbb93F8*`j;b4w#&{a>fvY}*9+9XEbjPo$X2TLFb|u$@ta5HBMF)? zs5a(i67b_MW-t73AUe^0wyIP{?-zQv3smVsL*wmX!gyPUT$BRGI70tZjv3z_WZ&3o z%nf*XT&XI5#ET!p_n|{wT_doXUEbfzH|#zitl(IaWtQzWOIQ6w>rAXOR-A5>Y)n>z z7FsEq-CMOiKD<}^*m*zihE%=VY_>Brc{};NrsLbGin1L(+t}(IQuLsywZufn!Pq45 z>HLK%WaFrWIKOz7^zAY(l*&mANZ4%OVQ&7!Acz*114zEx*a`+p-N;V)7o7Zo+UYQX4(ieqDeXI#;_og zNi_V%nNV9Ym>4$8oBc4<#|`d{=lN#h=jRR5Qzxh2ELX}KZ!t>XOu&uYm+L+7UWh-} zx7>`j9?jDbTZq zm7vF{HD%=HlQbR%LlQV*ApSHYSZ>hXw9)Y^1vB!T3x=)835L4((5WiLK)Mv$lI}bs zPG8kl(1Qp2BK!4L)~4l5G1lg^+ugx5H2=KgH4l@wv7W2ay7z_g2dAX}{^)>(9oo!{ zf~^KnMzW6wQ^sM32@Yw=t|y3bVEVT^U#*)ECJvCu4GU#If9&ZoH`OlDbg-L#Jfa-$ ziSdTpoZJmoGu?n9@QmuF3K%m)l=Jl}{54kw`Wd}bC!+o?SPUK3&Nk;SBCI8mrvAP6 zW`)!{Wzug$VQ0az`>pM*T)l14yr+^9j}+t1pMF+}^KkicfeCYVW7b}?CeJp!u(#D` z@-v1tSt+2rwiNJ?J+N+V{qdke%Y z6SC2UKF2JE3rC^Ri)|TUsxkre5~jQvltLffeA%wv=+*Zp8j((+_|IqBtv|aD$qvcM z*zsX2eLIFqd$F7{F{{*Vswz{oTVu?@t$mjpP(X$KLA3CRvPF#w*5Y@(*auF zjP_mg2CUhscYqHOxIn4Z+{F1Uq`&)xS-nOc2U+bHc9)Me#%gC43WyOWAr|Xq0!Y^ z^?xE?u-H}?O|I}1-zGHiiEwVG9w&1M-Y>%-s*|hgc-7?am~|jWF&GO=KGK{m*XMP( z9%uDNF*}VMkAp3I>^JFl?Y?H8<;ak$!hl8nm)mGS^ve*tIYYJX*rv)QkFU?4<(d5j(Is!>MV+S)v2 zAhTMZz7EOHoWRQ6fi;CG^WWT}Ylt}Suf2FSNUjI@T2?}(HJx#_!uZ~QHYIHEfZZx{ zmE`>o5@>X{|K8>pzY916k3xzy@uvmcKo#p!4bG%OV96JqDtR4c1~O&r(ksKB{JN7i%y)mtbW(1G{iu6V9DVJin9MMub=`w~~2NS|LaJ#tD#vMnm>E z0#8@)uLdF;pacupis=njEAXZj;p_B zNxCrxN{XZ05L7K2ybF5@gho%pg1fda{6H2G3tf~F)5W$b1Yz@g+<2ACBx*jte{b(> zZY6uoa`*21Om@Bg(*!lN(40VHQ^m>VAZTc&5gPoIF3~urD3{D`mBii6UaemBYZ63d zjQFo4PjRN~wzHAUPe6YH+Md9h7sVr?5+e6HdVruQ6oOWM9^5=emFnb zqIWcXer6?X%^-f8NPA6LK>bGCLQh$FFQl_gIxajNpy|K<+&Ce>vx)bn8IaG zlvkVLI?vfEMF#AN2HdCZ(KaSwRCSM+{CcUsjV>CU_vWkDnx4h(=s}WkqqWWZXi_RQ z>BQ~bhCw&|*?hXEa4B}6HKKJ|SAIK@%f&-ek`?#%j+>^Y#9zxhMY&==-OS*hbHbMryEqHEng0Bm57{dbm#@n|c%EV)dpzbEz8-QWF>XGkKPCRF(YHZ#8j? zz6MD*%r&O1giHrORq52iY})^S{@y@p$BcX&)MsRP9M`I&z*JVs106Y1)Kv%x6I6Doo`C>tMPQ}Np%fwo zbpvc?|1t1nYwi3B$I@ITGKU4^M4ni{FoZdi?ugnbUOrR0FA>kb`&H(e0@H{bOg0fg zVxL`-z?q7R7NO~LWO{P!=|NQ`+&}Gxu{B! zX;V^6|9;RUr*RxnInZT)R!*HX8BZ@y&%`$?TZ7fwe|zK?+YnjtZE#6*BMP68-M@6y zu;Z;a+39I`v1##I>FqX^(3FC@xXn*q#u-@_t=Fep^7RHzcIGFtU;qda^#^K)w)@I6 zRK_Y*EH1ZE^9Tn@`v)rH>NV~|ChB@4d2vu`DNRpwZEYMb`r+n%L@3>wfM$5o#r?M@PTPmSlSod>ZJmWw>Cw zy*4m(Gw?==8Ir;Lu%;iIq~9}%eax4M>p9OF_PC4cu;u5+nLrM{z24p1S@OL30E~EH z$3B6#X^{;KyXk1ge4t65;C~#%tjPGWdiwB}9GNkSdEDIeWl&_9OTqSe6fFJYly7VO z&}-D{;RbgTcfj%73Wij)wyx!`;r9^|e|&k?=W36llFNQVRE!FE8>;N$A|u|T#@U}> zO93ff1F(f8mi3m1EK}b4`$%D4NR93;MT9pK9Bypjqu)nEqd$M7J*IX�x0qhQ9wu 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%0ACfo-R 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 str: + payload = handle.read() + if isinstance(payload, str): + return payload + if isinstance(payload, (bytes, bytearray)): + return bytes(payload).decode("utf-8", errors="replace") + return str(payload) + + +class _SandboxWorkspaceEditor: + def __init__(self, session: BaseSandboxSession) -> None: + self._session = session + + async def create_file(self, operation: ApplyPatchOperation) -> ApplyPatchResult: + target = self._resolve_path(operation.path) + content = apply_diff("", operation.diff or "", mode="create") + await self._session.mkdir(target.parent, parents=True) + await self._session.write(target, io.BytesIO(content.encode("utf-8"))) + return ApplyPatchResult(output=f"Created {self._display_path(target)}") + + async def update_file(self, operation: ApplyPatchOperation) -> ApplyPatchResult: + target = self._resolve_path(operation.path) + handle = await self._session.read(target) + try: + original = _read_text(handle) + finally: + handle.close() + updated = apply_diff(original, operation.diff or "") + await self._session.write(target, io.BytesIO(updated.encode("utf-8"))) + return ApplyPatchResult(output=f"Updated {self._display_path(target)}") + + async def delete_file(self, operation: ApplyPatchOperation) -> ApplyPatchResult: + target = self._resolve_path(operation.path) + await self._session.rm(target) + return ApplyPatchResult(output=f"Deleted {self._display_path(target)}") + + def _resolve_path(self, raw_path: str) -> Path: + return self._session.normalize_path(raw_path) + + def _display_path(self, path: Path) -> str: + root = Path(self._session.state.manifest.root) + return path.relative_to(root).as_posix() + + +class WorkspaceApplyPatchCapability(Capability): + """Expose the hosted apply_patch tool against the active sandbox workspace.""" + + def __init__(self) -> None: + super().__init__(type="workspace_apply_patch") + self._session: BaseSandboxSession | None = None + + def bind(self, session: BaseSandboxSession) -> None: + self._session = session + + def tools(self) -> list[Tool]: + if self._session is None: + return [] + return [ApplyPatchTool(editor=_SandboxWorkspaceEditor(self._session))] + + async def instructions(self, manifest: Manifest) -> str | None: + _ = manifest + return ( + "Use the `apply_patch` tool for workspace text edits when you need to create or " + "update files inside the sandbox. Prefer saving final outputs in the requested " + "workspace directories instead of describing edits without writing them." + ) diff --git a/examples/sandbox/tax_prep.py b/examples/sandbox/tax_prep.py new file mode 100644 index 0000000000..38112e2e5f --- /dev/null +++ b/examples/sandbox/tax_prep.py @@ -0,0 +1,264 @@ +from __future__ import annotations + +import argparse +import asyncio +import sys +from pathlib import Path +from typing import cast + +from openai.types.responses import ResponseTextDeltaEvent + +from agents import Runner +from agents.items import TResponseInputItem +from agents.run import RunConfig +from agents.sandbox import Manifest, SandboxAgent, SandboxRunConfig +from agents.sandbox.capabilities import Skills +from agents.sandbox.entries import Dir, GitRepo, LocalFile + +if __package__ is None or __package__ == "": + sys.path.insert(0, str(Path(__file__).resolve().parents[2])) + +from examples.sandbox.misc.workspace_apply_patch import WorkspaceApplyPatchCapability +from examples.sandbox.misc.workspace_shell import WorkspaceShellCapability + +DATA_PATH = Path(__file__).resolve().parent / "data" +W2_PATH = DATA_PATH / "sample_w2.pdf" +FORM_1040_PATH = DATA_PATH / "f1040.pdf" +DEFAULT_IMAGE = "tax-prep:latest" +DEFAULT_SKILLS_REPO = "sdcoffey/tax-prep-skills" +DEFAULT_SKILLS_REF = "main" +DEFAULT_QUESTION = "Please generate a 1040 for filing year 2025." + +INSTRUCTIONS = """ +You are a federal tax filing agent. Your job is to compute year-end taxes and +produce a filled-out Form 1040 for the specified tax year using the user's +provided documents. Use only the information in the supplied files. If required +data is missing or unclear, ask follow-up questions or note explicit +assumptions. Save the finalized, filled PDF in the `output/` directory and +provide a short summary of key amounts such as income, deductions, tax, and +refund or amount due. + +This is a demo, so assume the following unless the workspace says otherwise: +1. Filing status is single. +2. SSN is 123-45-6789. +3. Date of birth is 1991-01-01. +4. There are no other income documents. +5. If a minor data point is still needed, make up a clearly synthetic test value. + +Use the `federal-tax-prep` skill to accomplish this task. +""".strip() + + +def _require_docker_dependency(): + try: + from docker import from_env as docker_from_env # type: ignore[import-untyped] + except Exception as exc: # pragma: no cover - import path depends on local Docker setup + raise SystemExit( + "Docker-backed runs require the Docker SDK.\n" + "Install the repo dependencies with: make sync" + ) from exc + + from agents.sandbox.sandboxes.docker import DockerSandboxClient, DockerSandboxClientOptions + + return docker_from_env, DockerSandboxClient, DockerSandboxClientOptions + + +def _build_manifest() -> Manifest: + return Manifest( + entries={ + "taxpayer_data": Dir( + children={"sample_w2.pdf": LocalFile(src=W2_PATH)}, + description="Taxpayer income documents such as W-2s and 1099s.", + ), + "reference_forms": Dir( + children={"f1040.pdf": LocalFile(src=FORM_1040_PATH)}, + description="Blank tax forms the agent can use as templates.", + ), + "output": Dir(description="Write finalized tax documents here."), + } + ) + + +def _build_agent(*, model: str, skills_repo: str, skills_ref: str) -> SandboxAgent: + return SandboxAgent( + name="Tax Prep Assistant", + model=model, + instructions=INSTRUCTIONS, + developer_instructions=( + "Inspect the workspace before answering. Keep final explanations concise, and make " + "sure the final filled files are actually written into `output/`." + ), + default_manifest=_build_manifest(), + capabilities=[ + WorkspaceShellCapability(), + WorkspaceApplyPatchCapability(), + Skills( + from_=GitRepo(repo=skills_repo, ref=skills_ref), + ), + ], + codex=False, + ) + + +async def _copy_output_dir( + *, + session, + destination_root: Path, +) -> list[Path]: + destination_root.mkdir(parents=True, exist_ok=True) + remote_output_root = session.normalize_path("output") + + pending_dirs = [remote_output_root] + copied_files: list[Path] = [] + while pending_dirs: + current_dir = pending_dirs.pop() + for entry in await session.ls(current_dir): + entry_path = Path(entry.path) + if entry.is_dir(): + pending_dirs.append(entry_path) + continue + + relative_path = entry_path.relative_to(remote_output_root) + local_path = destination_root / relative_path + local_path.parent.mkdir(parents=True, exist_ok=True) + + handle = await session.read(entry_path) + try: + payload = handle.read() + finally: + handle.close() + + if isinstance(payload, str): + local_path.write_text(payload, encoding="utf-8") + else: + local_path.write_bytes(bytes(payload)) + copied_files.append(local_path) + + return copied_files + + +async def _run_turn( + *, + agent: SandboxAgent, + input_items: list[TResponseInputItem], + run_config: RunConfig, +) -> list[TResponseInputItem]: + stream_result = Runner.run_streamed(agent, input_items, run_config=run_config) + saw_text_delta = 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) + continue + + if event.type == "run_item_stream_event" and event.name == "tool_called": + raw_item = getattr(event.item, "raw_item", None) + tool_name = "" + if isinstance(raw_item, dict): + tool_name = cast(str, raw_item.get("name") or raw_item.get("type") or "") + else: + tool_name = cast( + str, + getattr(raw_item, "name", None) or getattr(raw_item, "type", None) or "", + ) + if tool_name: + if saw_text_delta: + print() + saw_text_delta = False + print(f"[tool call] {tool_name}") + + if saw_text_delta: + print() + + return stream_result.to_input_list() + + +async def main( + *, + model: str, + image: str, + question: str, + output_dir: Path, + skills_repo: str, + skills_ref: str, +) -> None: + docker_from_env, DockerSandboxClient, DockerSandboxClientOptions = _require_docker_dependency() + agent = _build_agent(model=model, skills_repo=skills_repo, skills_ref=skills_ref) + client = DockerSandboxClient(docker_from_env()) + session = await client.create( + manifest=agent.default_manifest, + codex=agent.codex, + options=DockerSandboxClientOptions(image=image), + ) + + run_config = RunConfig( + sandbox=SandboxRunConfig(session=session), + workflow_name="Sandbox tax prep demo", + ) + + conversation: list[TResponseInputItem] = [{"role": "user", "content": question}] + + try: + async with session: + conversation = await _run_turn( + agent=agent, + input_items=conversation, + run_config=run_config, + ) + + while True: + try: + additional_input = input("> ") + except (EOFError, KeyboardInterrupt): + break + + conversation.append({"role": "user", "content": additional_input}) + conversation = await _run_turn( + agent=agent, + input_items=conversation, + run_config=run_config, + ) + + copied_files = await _copy_output_dir(session=session, destination_root=output_dir) + finally: + await client.delete(session) + + print(f"\nCopied {len(copied_files)} file(s) to {output_dir}") + for copied_file in copied_files: + print(copied_file) + + +if __name__ == "__main__": + parser = argparse.ArgumentParser() + parser.add_argument("--model", default="gpt-5.4", help="Model name to use.") + parser.add_argument("--image", default=DEFAULT_IMAGE, help="Docker image for the sandbox.") + parser.add_argument("--question", default=DEFAULT_QUESTION, help="Prompt to send to the agent.") + parser.add_argument( + "--output-dir", + default="tax-prep-results", + help="Local directory where files from sandbox output/ will be copied.", + ) + parser.add_argument( + "--skills-repo", + default=DEFAULT_SKILLS_REPO, + help="GitHub repo in owner/name form for the skills bundle.", + ) + parser.add_argument( + "--skills-ref", + default=DEFAULT_SKILLS_REF, + help="Git ref for the skills bundle.", + ) + args = parser.parse_args() + + asyncio.run( + main( + model=args.model, + image=args.image, + question=args.question, + output_dir=Path(args.output_dir).resolve(), + skills_repo=args.skills_repo, + skills_ref=args.skills_ref, + ) + ) diff --git a/src/agents/extensions/sandbox/sandboxes/modal.py b/src/agents/extensions/sandbox/sandboxes/modal.py index e4621a063f..c09d7c2b9a 100644 --- a/src/agents/extensions/sandbox/sandboxes/modal.py +++ b/src/agents/extensions/sandbox/sandboxes/modal.py @@ -65,6 +65,7 @@ _DEFAULT_TIMEOUT_S = 30.0 _DEFAULT_IMAGE_TAG = "python:3.11-slim" _DEFAULT_SNAPSHOT_FILESYSTEM_TIMEOUT_S = 60.0 +_MODAL_STDIN_CHUNK_SIZE = 8 * 1024 * 1024 WorkspacePersistenceMode = Literal["tar", "snapshot_filesystem"] @@ -78,6 +79,19 @@ R = TypeVar("R") +def _write_process_stdin(proc: ContainerProcess[bytes], data: bytes | bytearray) -> None: + """ + Stream stdin to Modal in bounded chunks so command-router backed writers do not overflow. + """ + + view = memoryview(data) + for start in range(0, len(view), _MODAL_STDIN_CHUNK_SIZE): + proc.stdin.write(view[start : start + _MODAL_STDIN_CHUNK_SIZE]) + proc.stdin.drain() + proc.stdin.write_eof() + proc.stdin.drain() + + @dataclass(frozen=True) class ModalSandboxClientOptions: app_name: str @@ -436,9 +450,7 @@ def _run() -> None: # Stream bytes into `cat > file` to avoid quoting/binary issues. cmd = ["sh", "-lc", f"cat > {shlex.quote(str(workspace_path))}"] proc = self._sandbox.exec(*cmd, text=False) - proc.stdin.write(bytes(payload)) - proc.stdin.write_eof() - proc.stdin.drain() + _write_process_stdin(proc, payload) exit_code = proc.wait() if exit_code != 0: stderr: bytes = proc.stderr.read() @@ -516,9 +528,7 @@ async def _restore_ephemeral_paths() -> WorkspaceArchiveReadError | None: def _restore_ephemeral() -> None: assert self._sandbox is not None proc = self._sandbox.exec("tar", "xf", "-", "-C", str(root), text=False) - proc.stdin.write(backup) - proc.stdin.write_eof() - proc.stdin.drain() + _write_process_stdin(proc, backup) exit_code = proc.wait() if exit_code != 0: stderr: bytes = proc.stderr.read() @@ -810,9 +820,7 @@ def _run() -> None: assert self._sandbox is not None self._sandbox.exec("mkdir", "-p", "--", str(root), text=False).wait() proc = self._sandbox.exec("tar", "xf", "-", "-C", str(root), text=False) - proc.stdin.write(bytes(raw)) - proc.stdin.write_eof() - proc.stdin.drain() + _write_process_stdin(proc, raw) exit_code = proc.wait() if exit_code != 0: stderr: bytes = proc.stderr.read() diff --git a/src/agents/sandbox/capabilities/__init__.py b/src/agents/sandbox/capabilities/__init__.py index 62da00db26..0c13ccb59c 100644 --- a/src/agents/sandbox/capabilities/__init__.py +++ b/src/agents/sandbox/capabilities/__init__.py @@ -1,3 +1,4 @@ from .capability import Capability +from .skills import Skill, Skills -__all__ = ["Capability"] +__all__ = ["Capability", "Skill", "Skills"] diff --git a/src/agents/sandbox/capabilities/skills.py b/src/agents/sandbox/capabilities/skills.py new file mode 100644 index 0000000000..81582277df --- /dev/null +++ b/src/agents/sandbox/capabilities/skills.py @@ -0,0 +1,270 @@ +from __future__ import annotations + +from collections.abc import Mapping, Sequence +from pathlib import Path +from typing import Any + +from pydantic import BaseModel, ConfigDict, Field, field_validator + +from ..entries import BaseEntry, Dir, File, LocalFile +from ..errors import SkillsConfigError +from ..manifest import Manifest +from .capability import Capability + +_SKILLS_ROOT = Path(".agents/skills") + + +def _validate_relative_path( + value: str | Path, + *, + field_name: str, + context: Mapping[str, object] | None = None, +) -> Path: + rel = value if isinstance(value, Path) else Path(value) + if rel.is_absolute(): + raise SkillsConfigError( + message=f"{field_name} must be a relative path", + context={ + "field": field_name, + "path": str(rel), + "reason": "absolute", + **(context or {}), + }, + ) + if ".." in rel.parts: + raise SkillsConfigError( + message=f"{field_name} must not escape the skills root", + context={ + "field": field_name, + "path": str(rel), + "reason": "escape_root", + **(context or {}), + }, + ) + if rel.parts in [(), (".",)]: + raise SkillsConfigError( + message=f"{field_name} must be non-empty", + context={"field": field_name, "path": str(rel), "reason": "empty", **(context or {})}, + ) + return rel + + +def _manifest_entry_paths(manifest: Manifest) -> set[Path]: + return {key if isinstance(key, Path) else Path(key) for key in manifest.entries} + + +def _get_manifest_entry_by_path(manifest: Manifest, path: Path) -> BaseEntry | None: + for key, entry in manifest.entries.items(): + normalized = key if isinstance(key, Path) else Path(key) + if normalized == path: + return entry + return None + + +class Skill(BaseModel): + model_config = ConfigDict(arbitrary_types_allowed=True) + + name: str + description: str + content: str | bytes | BaseEntry + + compatibility: str | None = Field(default=None) + scripts: dict[str | Path, BaseEntry] = Field(default_factory=dict) + references: dict[str | Path, BaseEntry] = Field(default_factory=dict) + assets: dict[str | Path, BaseEntry] = Field(default_factory=dict) + + deferred: bool = Field(default=False) + + @field_validator("content", mode="before") + @classmethod + def _parse_content(cls, value: object) -> object: + if isinstance(value, Mapping): + return BaseEntry.parse(value) + return value + + @field_validator("scripts", "references", "assets", mode="before") + @classmethod + def _parse_entry_map(cls, value: object) -> dict[str | Path, BaseEntry]: + if value is None: + return {} + if not isinstance(value, Mapping): + raise TypeError(f"Artifact mapping must be a mapping, got {type(value).__name__}") + return {key: BaseEntry.parse(entry) for key, entry in value.items()} + + def model_post_init(self, context: Any, /) -> None: + _ = context + skill_context: dict[str, object] = {"skill_name": self.name} + _validate_relative_path(self.name, field_name="name", context=skill_context) + + content_artifact = self.content_artifact() + if not isinstance(content_artifact, (File, LocalFile)): + raise SkillsConfigError( + message="skill content must be file-like", + context={ + "field": "content", + "skill_name": self.name, + "content_type": content_artifact.type, + }, + ) + + self.scripts = self._normalize_entry_map(self.scripts, field_name="scripts") + self.references = self._normalize_entry_map(self.references, field_name="references") + self.assets = self._normalize_entry_map(self.assets, field_name="assets") + + def _normalize_entry_map( + self, + entries: Mapping[str | Path, BaseEntry], + *, + field_name: str, + ) -> dict[str | Path, BaseEntry]: + normalized: dict[str | Path, BaseEntry] = {} + seen_paths: set[str] = set() + for key, artifact in entries.items(): + rel = _validate_relative_path( + key, + field_name=field_name, + context={"skill_name": self.name, "entry_path": str(key)}, + ) + rel_str = rel.as_posix() + if rel_str in seen_paths: + raise SkillsConfigError( + message=f"duplicate entry path in skill {field_name}", + context={ + "skill_name": self.name, + "field": field_name, + "entry_path": rel_str, + }, + ) + seen_paths.add(rel_str) + normalized[rel_str] = artifact + + return normalized + + def content_artifact(self) -> BaseEntry: + if isinstance(self.content, bytes): + return File(content=self.content) + if isinstance(self.content, str): + return File(content=self.content.encode("utf-8")) + return self.content + + def as_dir_entry(self) -> Dir: + children: dict[str | Path, BaseEntry] = {"SKILL.md": self.content_artifact()} + if self.scripts: + children["scripts"] = Dir(children=self.scripts) + if self.references: + children["references"] = Dir(children=self.references) + if self.assets: + children["assets"] = Dir(children=self.assets) + return Dir(children=children) + + +class Skills(Capability): + """Mount skills into a Codex auto-discovery root inside the sandbox.""" + + type: str = "skills" + skills: list[Skill] + from_: BaseEntry | None + + def __init__( + self, + *, + skills: Sequence[Skill | Mapping[str, object]] | None = None, + from_: BaseEntry | Mapping[str, object] | None = None, + ) -> None: + super().__init__(type="skills") + self.skills = [self._coerce_skill(skill) for skill in (skills or [])] + self.from_ = self._coerce_entry(from_) + self._validate() + + @staticmethod + def _coerce_skill(skill: Skill | Mapping[str, object]) -> Skill: + if isinstance(skill, Skill): + return skill + return Skill.model_validate(dict(skill)) + + @staticmethod + def _coerce_entry(entry: BaseEntry | Mapping[str, object] | None) -> BaseEntry | None: + if entry is None or isinstance(entry, BaseEntry): + return entry + return BaseEntry.parse(entry) + + def _validate(self) -> None: + if not self.skills and self.from_ is None: + raise SkillsConfigError( + message="skills capability requires `skills` or `from_`", + context={"field": "skills"}, + ) + if self.skills and self.from_ is not None: + raise SkillsConfigError( + message="skills capability does not allow both `skills` and `from_` together", + context={"field": "skills", "has_from": True}, + ) + + if self.from_ is not None and not self.from_.is_dir: + raise SkillsConfigError( + message="`from_` must be a directory-like artifact", + context={"field": "from_", "artifact_type": self.from_.type}, + ) + + seen_names: set[Path] = set() + for skill in self.skills: + rel = _validate_relative_path( + skill.name, + field_name="skills[].name", + context={"skill_name": skill.name}, + ) + if rel in seen_names: + raise SkillsConfigError( + message=f"duplicate skill name: {skill.name}", + context={"field": "skills[].name", "skill_name": skill.name}, + ) + seen_names.add(rel) + + def process_manifest(self, manifest: Manifest) -> Manifest: + skills_root = _SKILLS_ROOT + existing_paths = _manifest_entry_paths(manifest) + + if self.from_ is not None: + if skills_root in existing_paths: + existing_entry = _get_manifest_entry_by_path(manifest, skills_root) + if existing_entry is None: + raise SkillsConfigError( + message="skills root path lookup failed", + context={"path": str(skills_root), "source": "from_"}, + ) + if existing_entry.is_dir: + return manifest + raise SkillsConfigError( + message="skills root path already exists in manifest", + context={ + "path": str(skills_root), + "source": "from_", + "existing_type": existing_entry.type, + }, + ) + manifest.entries[skills_root] = self.from_ + existing_paths.add(skills_root) + + for skill in self.skills: + relative_path = skills_root / Path(skill.name) + rendered_skill = skill.as_dir_entry() + if relative_path in existing_paths: + existing_entry = _get_manifest_entry_by_path(manifest, relative_path) + if existing_entry is None: + raise SkillsConfigError( + message="skill path lookup failed", + context={"path": str(relative_path), "skill_name": skill.name}, + ) + if existing_entry == rendered_skill: + continue + raise SkillsConfigError( + message="skill path already exists in manifest", + context={"path": str(relative_path), "skill_name": skill.name}, + ) + manifest.entries[relative_path] = rendered_skill + existing_paths.add(relative_path) + + return manifest + + +__all__ = ["Skill", "Skills"] diff --git a/src/agents/sandbox/codex_config.py b/src/agents/sandbox/codex_config.py index 77322a062b..d63ab891ae 100644 --- a/src/agents/sandbox/codex_config.py +++ b/src/agents/sandbox/codex_config.py @@ -9,7 +9,7 @@ from .manifest import Manifest from .session.sandbox_session_state import SandboxSessionState -DEFAULT_CODEX_PATH = "~/.codex/codex" +DEFAULT_CODEX_PATH = ".codex_bin/codex" # TODO: this should eventually be sourced from the pinned version of codex-python-sdk DEFAULT_CODEX_VERSION = "0.114.0" SandboxSessionStateT = TypeVar("SandboxSessionStateT", bound=SandboxSessionState) diff --git a/src/agents/sandbox/entries/codex.py b/src/agents/sandbox/entries/codex.py index e59f14f7c8..1668e974b9 100644 --- a/src/agents/sandbox/entries/codex.py +++ b/src/agents/sandbox/entries/codex.py @@ -27,6 +27,7 @@ class Codex(BaseEntry): type: Literal["codex"] = "codex" + ephemeral: bool = True version: str = "latest" async def apply( diff --git a/src/agents/sandbox/errors.py b/src/agents/sandbox/errors.py index 445625095f..7ccc759e76 100644 --- a/src/agents/sandbox/errors.py +++ b/src/agents/sandbox/errors.py @@ -41,6 +41,7 @@ def __str__(self) -> str: MOUNT_MISSING_TOOL = "mount_missing_tool" MOUNT_FAILED = "mount_failed" MOUNT_CONFIG_INVALID = "mount_config_invalid" + SKILLS_CONFIG_INVALID = "skills_config_invalid" SNAPSHOT_PERSIST_ERROR = "snapshot_persist_error" SNAPSHOT_RESTORE_ERROR = "snapshot_restore_error" @@ -661,6 +662,25 @@ def __init__( ) +class SkillsConfigError(ConfigurationError): + """Skills capability configuration was invalid.""" + + def __init__( + self, + *, + message: str, + context: Mapping[str, object] | None = None, + cause: BaseException | None = None, + ) -> None: + super().__init__( + message=message, + error_code=ErrorCode.SKILLS_CONFIG_INVALID, + op="materialize", + context=_as_context(context), + cause=cause, + ) + + class SnapshotPersistError(SnapshotError): """Failed to persist snapshot bytes to durable storage.""" diff --git a/src/agents/sandbox/util/parse_utils.py b/src/agents/sandbox/util/parse_utils.py index 6c27705793..4b1ced206a 100644 --- a/src/agents/sandbox/util/parse_utils.py +++ b/src/agents/sandbox/util/parse_utils.py @@ -45,9 +45,14 @@ def parse_ls_la(output: str, *, base: str) -> list[FileEntry]: continue permissions = Permissions.from_str(permissions_str) + entry_path = ( + name + if name.startswith("/") + else (f"{base.rstrip('/')}/{name}" if base != "/" else f"/{name}") + ) entries.append( FileEntry( - path=f"{base.rstrip('/')}/{name}" if base != "/" else f"/{name}", + path=entry_path, permissions=permissions, owner=owner, group=group, diff --git a/tests/extensions/test_sandbox_modal.py b/tests/extensions/test_sandbox_modal.py index b60a33d11f..ad63961384 100644 --- a/tests/extensions/test_sandbox_modal.py +++ b/tests/extensions/test_sandbox_modal.py @@ -548,3 +548,190 @@ async def _fake_call_modal( assert commands == [] assert sandbox.snapshot_calls == 0 + + +@pytest.mark.asyncio +async def test_modal_write_chunks_large_payload_before_draining( + monkeypatch: pytest.MonkeyPatch, +) -> None: + modal_module, _create_calls, _registry_tags = _load_modal_module(monkeypatch) + + class _FakeWaitResult: + def wait(self) -> int: + return 0 + + class _FakeStdin: + def __init__(self, *, limit: int) -> None: + self._limit = limit + self._buffer = bytearray() + self.chunks: list[bytes] = [] + self.write_eof_calls = 0 + self.drain_calls = 0 + + def write(self, data: bytes | bytearray | memoryview) -> None: + rendered = bytes(data) + if len(self._buffer) + len(rendered) > self._limit: + raise BufferError("Buffer size exceed limit. Call drain to flush the buffer.") + self._buffer.extend(rendered) + + def write_eof(self) -> None: + self.write_eof_calls += 1 + + def drain(self) -> None: + self.chunks.append(bytes(self._buffer)) + self._buffer.clear() + self.drain_calls += 1 + + class _FakeProcess: + def __init__(self, *, limit: int) -> None: + self.stdin = _FakeStdin(limit=limit) + self.stderr = io.BytesIO(b"") + + def wait(self) -> int: + return 0 + + class _FakeSandbox: + object_id = "sb-123" + + def __init__(self) -> None: + self.processes: list[_FakeProcess] = [] + self.commands: list[tuple[object, ...]] = [] + + def exec(self, *command: object, **kwargs: object) -> object: + _ = kwargs + self.commands.append(command) + if command[:3] == ("mkdir", "-p", "--"): + return _FakeWaitResult() + process = _FakeProcess(limit=5) + self.processes.append(process) + return process + + sandbox = _FakeSandbox() + state = modal_module.ModalSandboxSessionState( + manifest=Manifest(root="/workspace"), + snapshot=modal_module.resolve_snapshot(None, "snapshot"), + app_name="sandbox-tests", + sandbox_id=sandbox.object_id, + ) + session = modal_module.ModalSandboxSession.from_state(state, sandbox=sandbox) + monkeypatch.setattr(modal_module, "_MODAL_STDIN_CHUNK_SIZE", 5) + + async def _fake_call_modal( + fn: Callable[..., object], + *args: object, + call_timeout: float | None = None, + **kwargs: object, + ) -> object: + _ = call_timeout + return fn(*args, **kwargs) + + monkeypatch.setattr(session, "_call_modal", _fake_call_modal) + + payload = b"abcdefghijklm" + await session.write(Path("nested/file.bin"), io.BytesIO(payload)) + + assert sandbox.commands == [ + ("mkdir", "-p", "--", "/workspace/nested"), + ("sh", "-lc", "cat > /workspace/nested/file.bin"), + ] + assert len(sandbox.processes) == 1 + assert sandbox.processes[0].stdin.chunks == [b"abcde", b"fghij", b"klm", b""] + assert sandbox.processes[0].stdin.write_eof_calls == 1 + assert sandbox.processes[0].stdin.drain_calls == 4 + + +@pytest.mark.asyncio +async def test_modal_hydrate_tar_chunks_large_payload_before_draining( + monkeypatch: pytest.MonkeyPatch, +) -> None: + modal_module, _create_calls, _registry_tags = _load_modal_module(monkeypatch) + + class _FakeWaitResult: + def wait(self) -> int: + return 0 + + class _FakeStdin: + def __init__(self, *, limit: int) -> None: + self._limit = limit + self._buffer = bytearray() + self.chunks: list[bytes] = [] + self.write_eof_calls = 0 + self.drain_calls = 0 + + def write(self, data: bytes | bytearray | memoryview) -> None: + rendered = bytes(data) + if len(self._buffer) + len(rendered) > self._limit: + raise BufferError("Buffer size exceed limit. Call drain to flush the buffer.") + self._buffer.extend(rendered) + + def write_eof(self) -> None: + self.write_eof_calls += 1 + + def drain(self) -> None: + self.chunks.append(bytes(self._buffer)) + self._buffer.clear() + self.drain_calls += 1 + + class _FakeProcess: + def __init__(self, *, limit: int) -> None: + self.stdin = _FakeStdin(limit=limit) + self.stderr = io.BytesIO(b"") + + def wait(self) -> int: + return 0 + + class _FakeSandbox: + object_id = "sb-123" + + def __init__(self) -> None: + self.processes: list[_FakeProcess] = [] + self.commands: list[tuple[object, ...]] = [] + + def exec(self, *command: object, **kwargs: object) -> object: + _ = kwargs + self.commands.append(command) + if command[:3] == ("mkdir", "-p", "--"): + return _FakeWaitResult() + process = _FakeProcess(limit=7) + self.processes.append(process) + return process + + sandbox = _FakeSandbox() + state = modal_module.ModalSandboxSessionState( + manifest=Manifest(root="/workspace"), + snapshot=modal_module.resolve_snapshot(None, "snapshot"), + app_name="sandbox-tests", + sandbox_id=sandbox.object_id, + ) + session = modal_module.ModalSandboxSession.from_state(state, sandbox=sandbox) + monkeypatch.setattr(modal_module, "_MODAL_STDIN_CHUNK_SIZE", 7) + + async def _fake_call_modal( + fn: Callable[..., object], + *args: object, + call_timeout: float | None = None, + **kwargs: object, + ) -> object: + _ = call_timeout + return fn(*args, **kwargs) + + monkeypatch.setattr(session, "_call_modal", _fake_call_modal) + + tar_payload = io.BytesIO() + with modal_module.tarfile.open(fileobj=tar_payload, mode="w") as tar: + info = modal_module.tarfile.TarInfo(name="large.txt") + contents = b"abcdefghijklmno" + info.size = len(contents) + tar.addfile(info, io.BytesIO(contents)) + tar_payload.seek(0) + + await session.hydrate_workspace(tar_payload) + + assert sandbox.commands == [ + ("mkdir", "-p", "--", "/workspace"), + ("tar", "xf", "-", "-C", "/workspace"), + ] + assert len(sandbox.processes) == 1 + assert b"".join(sandbox.processes[0].stdin.chunks[:-1]) == tar_payload.getvalue() + assert sandbox.processes[0].stdin.write_eof_calls == 1 + assert sandbox.processes[0].stdin.drain_calls >= 2 diff --git a/tests/test_example_workflows.py b/tests/test_example_workflows.py index 3e2dbcd8a8..bf39f78c61 100644 --- a/tests/test_example_workflows.py +++ b/tests/test_example_workflows.py @@ -28,7 +28,7 @@ from agents.agent import ToolsToFinalOutputResult from agents.items import TResponseInputItem from agents.tool import FunctionToolResult, function_tool -from examples.sandbox.docker_runner import _stream_event_banner +from examples.sandbox.basic import _stream_event_banner from examples.sandbox.sandbox_agents_as_tools import ( PricingPacketReview, RolloutRiskReview, diff --git a/tests/test_sandbox_manifest.py b/tests/test_sandbox_manifest.py index 5f493308b5..350b440564 100644 --- a/tests/test_sandbox_manifest.py +++ b/tests/test_sandbox_manifest.py @@ -158,6 +158,16 @@ def test_apply_codex_to_manifest_adds_codex_entry_at_configured_path() -> None: entry = validated[Path("tools/codex")] assert isinstance(entry, Codex) assert entry.version == DEFAULT_CODEX_VERSION + assert entry.ephemeral is True + + +def test_apply_codex_to_manifest_uses_reserved_default_codex_path() -> None: + manifest = apply_codex_to_manifest(Manifest(), True) + + validated = manifest.validated_entries() + + assert Path(".codex_bin/codex") in validated + assert manifest.ephemeral_persistence_paths() == {Path(".codex_bin/codex")} def test_apply_codex_to_manifest_treats_home_relative_path_as_workspace_relative() -> None: diff --git a/tests/test_sandbox_parse_utils.py b/tests/test_sandbox_parse_utils.py new file mode 100644 index 0000000000..9a9b81eef4 --- /dev/null +++ b/tests/test_sandbox_parse_utils.py @@ -0,0 +1,26 @@ +from agents.sandbox.files import EntryKind +from agents.sandbox.util.parse_utils import parse_ls_la + + +def test_parse_ls_la_preserves_absolute_file_paths() -> None: + output = "-rwxr-xr-x 1 root root 48915747 Jan 1 00:00 /workspace/.codex_bin/codex\n" + + entries = parse_ls_la(output, base="/workspace/.codex_bin/codex") + + assert len(entries) == 1 + assert entries[0].path == "/workspace/.codex_bin/codex" + assert entries[0].kind == EntryKind.FILE + + +def test_parse_ls_la_prefixes_directory_entries_with_base() -> None: + output = ( + "drwxr-xr-x 2 root root 4096 Jan 1 00:00 .\n" + "drwxr-xr-x 3 root root 4096 Jan 1 00:00 ..\n" + "-rw-r--r-- 1 root root 123 Jan 1 00:00 notes.md\n" + ) + + entries = parse_ls_la(output, base="/workspace/docs") + + assert len(entries) == 1 + assert entries[0].path == "/workspace/docs/notes.md" + assert entries[0].kind == EntryKind.FILE diff --git a/tests/test_sandbox_runtime.py b/tests/test_sandbox_runtime.py index e70b3f067b..72141c4747 100644 --- a/tests/test_sandbox_runtime.py +++ b/tests/test_sandbox_runtime.py @@ -878,7 +878,7 @@ async def test_runner_passes_codex_requirement_to_client_created_sessions() -> N manifest = client.create_kwargs["manifest"] assert manifest is not None manifest_paths = {manifest._coerce_rel_path(path) for path in manifest.entries} - assert Path(".codex/codex") in manifest_paths + assert Path(".codex_bin/codex") in manifest_paths @pytest.mark.asyncio diff --git a/tests/test_sandbox_skills_capability.py b/tests/test_sandbox_skills_capability.py new file mode 100644 index 0000000000..3809dc7bfa --- /dev/null +++ b/tests/test_sandbox_skills_capability.py @@ -0,0 +1,129 @@ +from __future__ import annotations + +from pathlib import Path + +import pytest + +from agents.sandbox import Manifest +from agents.sandbox.capabilities import Skill, Skills +from agents.sandbox.entries import Dir, File +from agents.sandbox.errors import SkillsConfigError + + +def _children_keys(entry: Dir) -> set[str]: + return {str(key if isinstance(key, Path) else Path(key)) for key in entry.children} + + +class TestSkillValidation: + def test_rejects_directory_content_artifact(self) -> None: + with pytest.raises(SkillsConfigError): + Skill(name="my-skill", description="desc", content=Dir()) + + +class TestSkillsValidation: + def test_requires_at_least_one_source(self) -> None: + with pytest.raises(SkillsConfigError): + Skills() + + def test_rejects_non_directory_from_artifact(self) -> None: + with pytest.raises(SkillsConfigError): + Skills(from_=File(content=b"not-a-dir")) + + def test_rejects_duplicate_skill_names(self) -> None: + with pytest.raises(SkillsConfigError): + Skills( + skills=[ + Skill(name="dup", description="first", content="a"), + Skill(name="dup", description="second", content="b"), + ] + ) + + def test_rejects_combining_literal_and_from_sources(self) -> None: + with pytest.raises(SkillsConfigError): + Skills( + from_=Dir( + children={"my-skill": Dir(children={"SKILL.md": File(content=b"imported")})} + ), + skills=[Skill(name="my-skill", description="desc", content="literal")], + ) + + +class TestSkillsManifest: + def test_literals_materialize_full_skill_structure(self) -> None: + capability = Skills( + skills=[ + Skill( + name="my-skill", + description="desc", + content="Use this skill.", + scripts={"run.sh": File(content=b"echo run")}, + references={"docs/readme.md": File(content=b"ref")}, + assets={"images/icon.txt": File(content=b"asset")}, + ) + ] + ) + + processed = capability.process_manifest(Manifest(root="/workspace")) + skill_entry = processed.entries[Path(".agents/skills/my-skill")] + assert isinstance(skill_entry, Dir) + assert _children_keys(skill_entry) == {"SKILL.md", "assets", "references", "scripts"} + + scripts = skill_entry.children["scripts"] + assert isinstance(scripts, Dir) + assert _children_keys(scripts) == {"run.sh"} + + references = skill_entry.children["references"] + assert isinstance(references, Dir) + assert _children_keys(references) == {"docs/readme.md"} + + assets = skill_entry.children["assets"] + assert isinstance(assets, Dir) + assert _children_keys(assets) == {"images/icon.txt"} + + def test_from_source_is_mapped_to_skills_root(self) -> None: + source = Dir(children={"imported": Dir(children={"SKILL.md": File(content=b"imported")})}) + capability = Skills(from_=source) + + processed = capability.process_manifest(Manifest(root="/workspace")) + assert processed.entries[Path(".agents/skills")] is source + + def test_literal_skills_are_idempotent_when_manifest_already_contains_same_skill(self) -> None: + capability = Skills( + skills=[ + Skill( + name="my-skill", + description="desc", + content="Use this skill.", + scripts={"run.sh": File(content=b"echo run")}, + ) + ] + ) + rendered_skill = capability.skills[0].as_dir_entry() + manifest = Manifest( + root="/workspace", + entries={".agents/skills/my-skill": rendered_skill}, + ) + + processed = capability.process_manifest(manifest) + assert processed.entries[".agents/skills/my-skill"] == rendered_skill + + def test_process_manifest_rejects_exact_path_collision(self) -> None: + capability = Skills(skills=[Skill(name="my-skill", description="desc", content="literal")]) + manifest = Manifest(root="/workspace", entries={Path(".agents/skills/my-skill"): Dir()}) + + with pytest.raises(SkillsConfigError): + capability.process_manifest(manifest) + + +class TestSkillsInstructions: + @pytest.mark.asyncio + async def test_instructions_return_none(self) -> None: + capability = Skills( + skills=[ + Skill(name="z-skill", description="z description", content="z"), + Skill(name="a-skill", description="a description", content="a"), + ] + ) + + instructions = await capability.instructions(Manifest(root="/workspace")) + assert instructions is None From 6b7b7b60fca9d344ee237a293052484b5d8a181a Mon Sep 17 00:00:00 2001 From: sdcoffey Date: Mon, 16 Mar 2026 17:18:07 -0700 Subject: [PATCH 11/11] feat: add remote sandbox app-server websocket client --- pyproject.toml | 6 + pyrightconfig.json | 1 + src/agents/sandbox/app_server/__init__.py | 10 + src/agents/sandbox/app_server/client.py | 507 ++ src/agents/sandbox/app_server/errors.py | 121 + .../sandbox/app_server/generated/__init__.py | 1 + .../generated/notification_registry.py | 108 + .../sandbox/app_server/generated/v2_all.py | 6136 +++++++++++++++++ .../sandbox/app_server/generated/v2_types.py | 23 + src/agents/sandbox/app_server/models.py | 99 + src/agents/sandbox/app_server/py.typed | 0 src/agents/sandbox/app_server/retry.py | 41 + tests/test_sandbox_app_server_client.py | 145 + uv.lock | 2 + 14 files changed, 7200 insertions(+) create mode 100644 src/agents/sandbox/app_server/__init__.py create mode 100644 src/agents/sandbox/app_server/client.py create mode 100644 src/agents/sandbox/app_server/errors.py create mode 100644 src/agents/sandbox/app_server/generated/__init__.py create mode 100644 src/agents/sandbox/app_server/generated/notification_registry.py create mode 100644 src/agents/sandbox/app_server/generated/v2_all.py create mode 100644 src/agents/sandbox/app_server/generated/v2_types.py create mode 100644 src/agents/sandbox/app_server/models.py create mode 100644 src/agents/sandbox/app_server/py.typed create mode 100644 src/agents/sandbox/app_server/retry.py create mode 100644 tests/test_sandbox_app_server_client.py diff --git a/pyproject.toml b/pyproject.toml index c82c57597c..0d37883e32 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -13,6 +13,7 @@ dependencies = [ "typing-extensions>=4.12.2, <5", "requests>=2.0, <3", "types-requests>=2.0, <3", + "websockets>=15.0, <16", "mcp>=1.19.0, <2; python_version >= '3.10'", ] classifiers = [ @@ -115,6 +116,7 @@ convention = "google" [tool.ruff.lint.per-file-ignores] "examples/**/*.py" = ["E501"] +"src/agents/sandbox/app_server/generated/v2_all.py" = ["E501"] [tool.mypy] strict = true @@ -126,6 +128,10 @@ disallow_untyped_calls = false module = "sounddevice.*" ignore_missing_imports = true +[[tool.mypy.overrides]] +module = "agents.sandbox.app_server.generated.*" +ignore_errors = true + [tool.coverage.run] source = ["src/agents"] omit = [ diff --git a/pyrightconfig.json b/pyrightconfig.json index 5ed525163c..9c1c39db87 100644 --- a/pyrightconfig.json +++ b/pyrightconfig.json @@ -1,5 +1,6 @@ { "include": ["src", "tests"], + "exclude": ["src/agents/sandbox/app_server/generated"], "extraPaths": ["."], "pythonVersion": "3.10", "typeCheckingMode": "basic", diff --git a/src/agents/sandbox/app_server/__init__.py b/src/agents/sandbox/app_server/__init__.py new file mode 100644 index 0000000000..aff63176b9 --- /dev/null +++ b/src/agents/sandbox/app_server/__init__.py @@ -0,0 +1,10 @@ +from .client import AppServerClient, AppServerConfig +from .errors import AppServerError, JsonRpcError, TransportClosedError + +__all__ = [ + "AppServerClient", + "AppServerConfig", + "AppServerError", + "JsonRpcError", + "TransportClosedError", +] diff --git a/src/agents/sandbox/app_server/client.py b/src/agents/sandbox/app_server/client.py new file mode 100644 index 0000000000..4150317099 --- /dev/null +++ b/src/agents/sandbox/app_server/client.py @@ -0,0 +1,507 @@ +from __future__ import annotations + +import json +import threading +import uuid +from collections import deque +from collections.abc import Iterable, Iterator +from dataclasses import dataclass +from typing import Any, Callable, TypeVar, cast + +from pydantic import BaseModel +from websockets.exceptions import ConnectionClosed +from websockets.sync.client import ClientConnection, connect + +from .errors import AppServerError, TransportClosedError, map_jsonrpc_error +from .generated.notification_registry import NOTIFICATION_MODELS +from .generated.v2_all import ( + AgentMessageDeltaNotification, + ModelListResponse, + ThreadArchiveResponse, + ThreadCompactStartResponse, + ThreadForkParams as V2ThreadForkParams, + ThreadForkResponse, + ThreadListParams as V2ThreadListParams, + ThreadListResponse, + ThreadReadResponse, + ThreadResumeParams as V2ThreadResumeParams, + ThreadResumeResponse, + ThreadSetNameResponse, + ThreadStartParams as V2ThreadStartParams, + ThreadStartResponse, + ThreadUnarchiveResponse, + TurnCompletedNotification, + TurnInterruptResponse, + TurnStartParams as V2TurnStartParams, + TurnStartResponse, + TurnSteerResponse, +) +from .models import ( + InitializeResponse, + JsonObject, + JsonValue, + Notification, + UnknownNotification, +) +from .retry import retry_on_overload + +ModelT = TypeVar("ModelT", bound=BaseModel) +ApprovalHandler = Callable[[str, JsonObject | None], JsonObject] + + +def _params_dict( + params: ( + V2ThreadStartParams + | V2ThreadResumeParams + | V2ThreadListParams + | V2ThreadForkParams + | V2TurnStartParams + | JsonObject + | None + ), +) -> JsonObject: + if params is None: + return {} + if hasattr(params, "model_dump"): + dumped = params.model_dump( + by_alias=True, + exclude_none=True, + mode="json", + ) + if not isinstance(dumped, dict): + raise TypeError("Expected model_dump() to return dict") + return dumped + if isinstance(params, dict): + return params + raise TypeError(f"Expected generated params model or dict, got {type(params).__name__}") + + +@dataclass(frozen=True) +class AppServerTransportOps: + ws_connect: Callable[..., ClientConnection] + + +def _default_transport_ops() -> AppServerTransportOps: + return AppServerTransportOps(ws_connect=connect) + + +@dataclass(slots=True) +class AppServerConfig: + websocket_url: str | None = None + websocket_headers: dict[str, str] | None = None + client_name: str = "codex_python_sdk" + client_title: str = "Codex Python SDK" + client_version: str = "0.2.0" + experimental_api: bool = True + websocket_open_timeout_s: float = 10.0 + websocket_recv_timeout_s: float | None = None + + +class AppServerClient: + """Synchronous typed JSON-RPC client for a remote `codex app-server` websocket.""" + + def __init__( + self, + config: AppServerConfig | None = None, + approval_handler: ApprovalHandler | None = None, + transport_ops: AppServerTransportOps | None = None, + ) -> None: + self.config = config or AppServerConfig() + self._approval_handler = approval_handler or self._default_approval_handler + self._ops = transport_ops or _default_transport_ops() + self._conn: ClientConnection | None = None + self._lock = threading.Lock() + self._turn_consumer_lock = threading.Lock() + self._active_turn_consumer: str | None = None + self._pending_notifications: deque[Notification] = deque() + + def __enter__(self) -> AppServerClient: + self.start() + return self + + def __exit__(self, _exc_type, _exc, _tb) -> None: + self.close() + + @property + def connected_url(self) -> str | None: + return self.config.websocket_url if self._conn is not None else None + + def start(self) -> None: + if self._conn is not None: + return + + websocket_url = self.config.websocket_url + if not websocket_url: + raise ValueError( + "AppServerConfig.websocket_url is required for remote app-server clients." + ) + + try: + self._conn = self._ops.ws_connect( + websocket_url, + additional_headers=self.config.websocket_headers, + open_timeout=self.config.websocket_open_timeout_s, + max_size=None, + ) + except Exception as exc: + raise TransportClosedError( + f"Failed to connect to app-server websocket `{websocket_url}`: {exc}" + ) from exc + + def close(self) -> None: + conn = self._conn + self._conn = None + self._active_turn_consumer = None + + if conn is not None: + try: + conn.close() + except Exception: + pass + + def initialize(self) -> InitializeResponse: + result = self.request( + "initialize", + { + "clientInfo": { + "name": self.config.client_name, + "title": self.config.client_title, + "version": self.config.client_version, + }, + "capabilities": { + "experimentalApi": self.config.experimental_api, + }, + }, + response_model=InitializeResponse, + ) + self.notify("initialized", None) + return result + + def request( + self, + method: str, + params: JsonObject | None, + *, + response_model: type[ModelT], + ) -> ModelT: + result = self._request_raw(method, params) + if not isinstance(result, dict): + raise AppServerError(f"{method} response must be a JSON object") + return response_model.model_validate(result) + + def _request_raw(self, method: str, params: JsonObject | None = None) -> JsonValue: + request_id = str(uuid.uuid4()) + self._write_message({"id": request_id, "method": method, "params": params or {}}) + + while True: + msg = self._read_message() + method_name = msg.get("method") + + if isinstance(method_name, str) and "id" in msg: + response = self._handle_server_request(msg) + self._write_message({"id": msg["id"], "result": response}) + continue + + if isinstance(method_name, str) and "id" not in msg: + self._pending_notifications.append( + self._coerce_notification(method_name, msg.get("params")) + ) + continue + + if msg.get("id") != request_id: + continue + + if "error" in msg: + err = msg["error"] + if isinstance(err, dict): + code_value = err.get("code", -32000) + code = int(code_value) if isinstance(code_value, (int, float, str)) else -32000 + raise map_jsonrpc_error( + code, + str(err.get("message", "unknown")), + err.get("data"), + ) + raise AppServerError("Malformed JSON-RPC error response") + + return msg.get("result") + + def notify(self, method: str, params: JsonObject | None = None) -> None: + self._write_message({"method": method, "params": params or {}}) + + def next_notification(self) -> Notification: + if self._pending_notifications: + return self._pending_notifications.popleft() + + while True: + msg = self._read_message() + method_name = msg.get("method") + if isinstance(method_name, str) and "id" in msg: + response = self._handle_server_request(msg) + self._write_message({"id": msg["id"], "result": response}) + continue + if isinstance(method_name, str) and "id" not in msg: + return self._coerce_notification(method_name, msg.get("params")) + + def acquire_turn_consumer(self, turn_id: str) -> None: + with self._turn_consumer_lock: + if self._active_turn_consumer is not None: + raise RuntimeError( + "Concurrent turn consumers are not yet supported in the experimental SDK. " + f"Client is already streaming turn {self._active_turn_consumer!r}; " + f"cannot start turn {turn_id!r} until the active consumer finishes." + ) + self._active_turn_consumer = turn_id + + def release_turn_consumer(self, turn_id: str) -> None: + with self._turn_consumer_lock: + if self._active_turn_consumer == turn_id: + self._active_turn_consumer = None + + def thread_start( + self, params: V2ThreadStartParams | JsonObject | None = None + ) -> ThreadStartResponse: + return self.request( + "thread/start", _params_dict(params), response_model=ThreadStartResponse + ) + + def thread_resume( + self, + thread_id: str, + params: V2ThreadResumeParams | JsonObject | None = None, + ) -> ThreadResumeResponse: + payload = {"threadId": thread_id, **_params_dict(params)} + return self.request("thread/resume", payload, response_model=ThreadResumeResponse) + + def thread_list( + self, params: V2ThreadListParams | JsonObject | None = None + ) -> ThreadListResponse: + return self.request("thread/list", _params_dict(params), response_model=ThreadListResponse) + + def thread_read(self, thread_id: str, include_turns: bool = False) -> ThreadReadResponse: + return self.request( + "thread/read", + {"threadId": thread_id, "includeTurns": include_turns}, + response_model=ThreadReadResponse, + ) + + def thread_fork( + self, + thread_id: str, + params: V2ThreadForkParams | JsonObject | None = None, + ) -> ThreadForkResponse: + payload = {"threadId": thread_id, **_params_dict(params)} + return self.request("thread/fork", payload, response_model=ThreadForkResponse) + + def thread_archive(self, thread_id: str) -> ThreadArchiveResponse: + return self.request( + "thread/archive", {"threadId": thread_id}, response_model=ThreadArchiveResponse + ) + + def thread_unarchive(self, thread_id: str) -> ThreadUnarchiveResponse: + return self.request( + "thread/unarchive", {"threadId": thread_id}, response_model=ThreadUnarchiveResponse + ) + + def thread_set_name(self, thread_id: str, name: str) -> ThreadSetNameResponse: + return self.request( + "thread/name/set", + {"threadId": thread_id, "name": name}, + response_model=ThreadSetNameResponse, + ) + + def thread_compact(self, thread_id: str) -> ThreadCompactStartResponse: + return self.request( + "thread/compact/start", + {"threadId": thread_id}, + response_model=ThreadCompactStartResponse, + ) + + def turn_start( + self, + thread_id: str, + input_items: list[JsonObject] | JsonObject | str, + params: V2TurnStartParams | JsonObject | None = None, + ) -> TurnStartResponse: + payload: JsonObject = { + **_params_dict(params), + "threadId": thread_id, + "input": cast(JsonValue, self._normalize_input_items(input_items)), + } + return self.request("turn/start", payload, response_model=TurnStartResponse) + + def turn_interrupt(self, thread_id: str, turn_id: str) -> TurnInterruptResponse: + return self.request( + "turn/interrupt", + {"threadId": thread_id, "turnId": turn_id}, + response_model=TurnInterruptResponse, + ) + + def turn_steer( + self, + thread_id: str, + expected_turn_id: str, + input_items: list[JsonObject] | JsonObject | str, + ) -> TurnSteerResponse: + payload: JsonObject = { + "threadId": thread_id, + "expectedTurnId": expected_turn_id, + "input": cast(JsonValue, self._normalize_input_items(input_items)), + } + return self.request( + "turn/steer", + payload, + response_model=TurnSteerResponse, + ) + + def model_list(self, include_hidden: bool = False) -> ModelListResponse: + return self.request( + "model/list", + {"includeHidden": include_hidden}, + response_model=ModelListResponse, + ) + + def request_with_retry_on_overload( + self, + method: str, + params: JsonObject | None, + *, + response_model: type[ModelT], + max_attempts: int = 3, + initial_delay_s: float = 0.25, + max_delay_s: float = 2.0, + ) -> ModelT: + return retry_on_overload( + lambda: self.request(method, params, response_model=response_model), + max_attempts=max_attempts, + initial_delay_s=initial_delay_s, + max_delay_s=max_delay_s, + ) + + def wait_for_turn_completed(self, turn_id: str) -> TurnCompletedNotification: + while True: + notification = self.next_notification() + if ( + notification.method == "turn/completed" + and isinstance(notification.payload, TurnCompletedNotification) + and notification.payload.turn.id == turn_id + ): + return notification.payload + + def stream_until_methods(self, methods: Iterable[str] | str) -> list[Notification]: + target_methods = {methods} if isinstance(methods, str) else set(methods) + out: list[Notification] = [] + while True: + notification = self.next_notification() + out.append(notification) + if notification.method in target_methods: + return out + + def stream_text( + self, + thread_id: str, + text: str, + params: V2TurnStartParams | JsonObject | None = None, + ) -> Iterator[AgentMessageDeltaNotification]: + started = self.turn_start(thread_id, text, params=params) + turn_id = started.turn.id + while True: + notification = self.next_notification() + if ( + notification.method == "item/agentMessage/delta" + and isinstance(notification.payload, AgentMessageDeltaNotification) + and notification.payload.turn_id == turn_id + ): + yield notification.payload + continue + if ( + notification.method == "turn/completed" + and isinstance(notification.payload, TurnCompletedNotification) + and notification.payload.turn.id == turn_id + ): + break + + def _coerce_notification(self, method: str, params: object) -> Notification: + params_dict = params if isinstance(params, dict) else {} + + model = NOTIFICATION_MODELS.get(method) + if model is None: + return Notification(method=method, payload=UnknownNotification(params=params_dict)) + + try: + payload = model.model_validate(params_dict) + except Exception: + return Notification(method=method, payload=UnknownNotification(params=params_dict)) + return Notification(method=method, payload=cast(Any, payload)) + + def _normalize_input_items( + self, + input_items: list[JsonObject] | JsonObject | str, + ) -> list[JsonObject]: + if isinstance(input_items, str): + return [{"type": "text", "text": input_items}] + if isinstance(input_items, dict): + return [input_items] + return input_items + + def _default_approval_handler(self, method: str, params: JsonObject | None) -> JsonObject: + if method == "item/commandExecution/requestApproval": + return {"decision": "accept"} + if method == "item/fileChange/requestApproval": + return {"decision": "accept"} + return {} + + def _handle_server_request(self, msg: dict[str, JsonValue]) -> JsonObject: + method = msg["method"] + params = msg.get("params") + if not isinstance(method, str): + return {} + return self._approval_handler( + method, + params if isinstance(params, dict) else None, + ) + + def _write_message(self, payload: JsonObject) -> None: + if self._conn is None: + raise TransportClosedError("app-server websocket is not connected") + + try: + with self._lock: + self._conn.send(json.dumps(payload), text=True) + except ConnectionClosed as exc: + raise TransportClosedError( + f"app-server websocket closed while sending. url={self.config.websocket_url!r}" + ) from exc + except Exception as exc: + raise AppServerError(f"Failed to send websocket message: {exc}") from exc + + def _read_message(self) -> dict[str, JsonValue]: + if self._conn is None: + raise TransportClosedError("app-server websocket is not connected") + + try: + frame = self._conn.recv(timeout=self.config.websocket_recv_timeout_s) + except ConnectionClosed as exc: + raise TransportClosedError( + f"app-server websocket closed while receiving. url={self.config.websocket_url!r}" + ) from exc + except Exception as exc: + raise AppServerError(f"Failed to receive websocket message: {exc}") from exc + + if isinstance(frame, bytes): + try: + raw_message = frame.decode("utf-8") + except UnicodeDecodeError as exc: + raise AppServerError( + "Received non-UTF-8 websocket binary frame from app-server" + ) from exc + else: + raw_message = frame + + try: + message = json.loads(raw_message) + except json.JSONDecodeError as exc: + raise AppServerError(f"Invalid JSON-RPC frame: {raw_message!r}") from exc + + if not isinstance(message, dict): + raise AppServerError(f"Invalid JSON-RPC payload: {message!r}") + return message diff --git a/src/agents/sandbox/app_server/errors.py b/src/agents/sandbox/app_server/errors.py new file mode 100644 index 0000000000..104e35f2e9 --- /dev/null +++ b/src/agents/sandbox/app_server/errors.py @@ -0,0 +1,121 @@ +from __future__ import annotations + +from typing import Any + + +class AppServerError(Exception): + """Base exception for SDK errors.""" + + +class JsonRpcError(AppServerError): + """Raw JSON-RPC error wrapper from the server.""" + + def __init__(self, code: int, message: str, data: Any = None): + super().__init__(f"JSON-RPC error {code}: {message}") + self.code = code + self.message = message + self.data = data + + +class TransportClosedError(AppServerError): + """Raised when the app-server transport closes unexpectedly.""" + + +class AppServerRpcError(JsonRpcError): + """Base typed error for JSON-RPC failures.""" + + +class ParseError(AppServerRpcError): + pass + + +class InvalidRequestError(AppServerRpcError): + pass + + +class MethodNotFoundError(AppServerRpcError): + pass + + +class InvalidParamsError(AppServerRpcError): + pass + + +class InternalRpcError(AppServerRpcError): + pass + + +class ServerBusyError(AppServerRpcError): + """Server is overloaded / unavailable and caller should retry.""" + + +class RetryLimitExceededError(ServerBusyError): + """Server exhausted internal retry budget for a retryable operation.""" + + +def _contains_retry_limit_text(message: str) -> bool: + lowered = message.lower() + return "retry limit" in lowered or "too many failed attempts" in lowered + + +def _is_server_overloaded(data: Any) -> bool: + if data is None: + return False + + if isinstance(data, str): + return data.lower() == "server_overloaded" + + if isinstance(data, dict): + direct = data.get("codex_error_info") or data.get("codexErrorInfo") or data.get("errorInfo") + if isinstance(direct, str) and direct.lower() == "server_overloaded": + return True + if isinstance(direct, dict): + for value in direct.values(): + if isinstance(value, str) and value.lower() == "server_overloaded": + return True + for value in data.values(): + if _is_server_overloaded(value): + return True + + if isinstance(data, list): + return any(_is_server_overloaded(value) for value in data) + + return False + + +def map_jsonrpc_error(code: int, message: str, data: Any = None) -> JsonRpcError: + """Map a raw JSON-RPC error into a richer SDK exception class.""" + + if code == -32700: + return ParseError(code, message, data) + if code == -32600: + return InvalidRequestError(code, message, data) + if code == -32601: + return MethodNotFoundError(code, message, data) + if code == -32602: + return InvalidParamsError(code, message, data) + if code == -32603: + return InternalRpcError(code, message, data) + + if -32099 <= code <= -32000: + if _is_server_overloaded(data): + if _contains_retry_limit_text(message): + return RetryLimitExceededError(code, message, data) + return ServerBusyError(code, message, data) + if _contains_retry_limit_text(message): + return RetryLimitExceededError(code, message, data) + return AppServerRpcError(code, message, data) + + return JsonRpcError(code, message, data) + + +def is_retryable_error(exc: BaseException) -> bool: + """True if the exception is a transient overload-style error.""" + + if isinstance(exc, ServerBusyError): + return True + + if isinstance(exc, JsonRpcError): + return _is_server_overloaded(exc.data) + + return False diff --git a/src/agents/sandbox/app_server/generated/__init__.py b/src/agents/sandbox/app_server/generated/__init__.py new file mode 100644 index 0000000000..d7b3f674b2 --- /dev/null +++ b/src/agents/sandbox/app_server/generated/__init__.py @@ -0,0 +1 @@ +"""Auto-generated Python types derived from the app-server schemas.""" diff --git a/src/agents/sandbox/app_server/generated/notification_registry.py b/src/agents/sandbox/app_server/generated/notification_registry.py new file mode 100644 index 0000000000..a714a7f222 --- /dev/null +++ b/src/agents/sandbox/app_server/generated/notification_registry.py @@ -0,0 +1,108 @@ +# Auto-generated by scripts/update_sdk_artifacts.py +# DO NOT EDIT MANUALLY. + +from __future__ import annotations + +from pydantic import BaseModel + +from .v2_all import ( + AccountLoginCompletedNotification, + AccountRateLimitsUpdatedNotification, + AccountUpdatedNotification, + AgentMessageDeltaNotification, + AppListUpdatedNotification, + CommandExecOutputDeltaNotification, + CommandExecutionOutputDeltaNotification, + ConfigWarningNotification, + ContextCompactedNotification, + DeprecationNoticeNotification, + ErrorNotification, + FileChangeOutputDeltaNotification, + FuzzyFileSearchSessionCompletedNotification, + FuzzyFileSearchSessionUpdatedNotification, + HookCompletedNotification, + HookStartedNotification, + ItemCompletedNotification, + ItemGuardianApprovalReviewCompletedNotification, + ItemGuardianApprovalReviewStartedNotification, + ItemStartedNotification, + McpServerOauthLoginCompletedNotification, + McpToolCallProgressNotification, + ModelReroutedNotification, + PlanDeltaNotification, + ReasoningSummaryPartAddedNotification, + ReasoningSummaryTextDeltaNotification, + ReasoningTextDeltaNotification, + ServerRequestResolvedNotification, + SkillsChangedNotification, + TerminalInteractionNotification, + ThreadArchivedNotification, + ThreadClosedNotification, + ThreadNameUpdatedNotification, + ThreadRealtimeClosedNotification, + ThreadRealtimeErrorNotification, + ThreadRealtimeItemAddedNotification, + ThreadRealtimeOutputAudioDeltaNotification, + ThreadRealtimeStartedNotification, + ThreadStartedNotification, + ThreadStatusChangedNotification, + ThreadTokenUsageUpdatedNotification, + ThreadUnarchivedNotification, + TurnCompletedNotification, + TurnDiffUpdatedNotification, + TurnPlanUpdatedNotification, + TurnStartedNotification, + WindowsSandboxSetupCompletedNotification, + WindowsWorldWritableWarningNotification, +) + +NOTIFICATION_MODELS: dict[str, type[BaseModel]] = { + "account/login/completed": AccountLoginCompletedNotification, + "account/rateLimits/updated": AccountRateLimitsUpdatedNotification, + "account/updated": AccountUpdatedNotification, + "app/list/updated": AppListUpdatedNotification, + "command/exec/outputDelta": CommandExecOutputDeltaNotification, + "configWarning": ConfigWarningNotification, + "deprecationNotice": DeprecationNoticeNotification, + "error": ErrorNotification, + "fuzzyFileSearch/sessionCompleted": FuzzyFileSearchSessionCompletedNotification, + "fuzzyFileSearch/sessionUpdated": FuzzyFileSearchSessionUpdatedNotification, + "hook/completed": HookCompletedNotification, + "hook/started": HookStartedNotification, + "item/agentMessage/delta": AgentMessageDeltaNotification, + "item/autoApprovalReview/completed": ItemGuardianApprovalReviewCompletedNotification, + "item/autoApprovalReview/started": ItemGuardianApprovalReviewStartedNotification, + "item/commandExecution/outputDelta": CommandExecutionOutputDeltaNotification, + "item/commandExecution/terminalInteraction": TerminalInteractionNotification, + "item/completed": ItemCompletedNotification, + "item/fileChange/outputDelta": FileChangeOutputDeltaNotification, + "item/mcpToolCall/progress": McpToolCallProgressNotification, + "item/plan/delta": PlanDeltaNotification, + "item/reasoning/summaryPartAdded": ReasoningSummaryPartAddedNotification, + "item/reasoning/summaryTextDelta": ReasoningSummaryTextDeltaNotification, + "item/reasoning/textDelta": ReasoningTextDeltaNotification, + "item/started": ItemStartedNotification, + "mcpServer/oauthLogin/completed": McpServerOauthLoginCompletedNotification, + "model/rerouted": ModelReroutedNotification, + "serverRequest/resolved": ServerRequestResolvedNotification, + "skills/changed": SkillsChangedNotification, + "thread/archived": ThreadArchivedNotification, + "thread/closed": ThreadClosedNotification, + "thread/compacted": ContextCompactedNotification, + "thread/name/updated": ThreadNameUpdatedNotification, + "thread/realtime/closed": ThreadRealtimeClosedNotification, + "thread/realtime/error": ThreadRealtimeErrorNotification, + "thread/realtime/itemAdded": ThreadRealtimeItemAddedNotification, + "thread/realtime/outputAudio/delta": ThreadRealtimeOutputAudioDeltaNotification, + "thread/realtime/started": ThreadRealtimeStartedNotification, + "thread/started": ThreadStartedNotification, + "thread/status/changed": ThreadStatusChangedNotification, + "thread/tokenUsage/updated": ThreadTokenUsageUpdatedNotification, + "thread/unarchived": ThreadUnarchivedNotification, + "turn/completed": TurnCompletedNotification, + "turn/diff/updated": TurnDiffUpdatedNotification, + "turn/plan/updated": TurnPlanUpdatedNotification, + "turn/started": TurnStartedNotification, + "windows/worldWritableWarning": WindowsWorldWritableWarningNotification, + "windowsSandbox/setupCompleted": WindowsSandboxSetupCompletedNotification, +} diff --git a/src/agents/sandbox/app_server/generated/v2_all.py b/src/agents/sandbox/app_server/generated/v2_all.py new file mode 100644 index 0000000000..300c798f3a --- /dev/null +++ b/src/agents/sandbox/app_server/generated/v2_all.py @@ -0,0 +1,6136 @@ +# generated by datamodel-codegen: +# filename: codex_app_server_protocol.v2.schemas.json + +from __future__ import annotations + +from enum import Enum +from typing import Annotated, Any, Literal + +from pydantic import BaseModel, ConfigDict, Field, RootModel + + +class CodexAppServerProtocolV2(BaseModel): + pass + model_config = ConfigDict( + populate_by_name=True, + ) + + +class AbsolutePathBuf(RootModel[str]): + model_config = ConfigDict( + populate_by_name=True, + ) + root: Annotated[ + str, + Field( + description="A path that is guaranteed to be absolute and normalized (though it is not guaranteed to be canonicalized or exist on the filesystem).\n\nIMPORTANT: When deserializing an `AbsolutePathBuf`, a base path must be set using [AbsolutePathBufGuard::new]. If no base path is set, the deserialization will fail unless the path being deserialized is already absolute." + ), + ] + + +class ApiKeyAccount(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + type: Annotated[Literal["apiKey"], Field(title="ApiKeyAccountType")] + + +class AccountLoginCompletedNotification(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + error: str | None = None + login_id: Annotated[str | None, Field(alias="loginId")] = None + success: bool + + +class AgentMessageDeltaNotification(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + delta: str + item_id: Annotated[str, Field(alias="itemId")] + thread_id: Annotated[str, Field(alias="threadId")] + turn_id: Annotated[str, Field(alias="turnId")] + + +class AnalyticsConfig(BaseModel): + model_config = ConfigDict( + extra="allow", + populate_by_name=True, + ) + enabled: bool | None = None + + +class AppBranding(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + category: str | None = None + developer: str | None = None + is_discoverable_app: Annotated[bool, Field(alias="isDiscoverableApp")] + privacy_policy: Annotated[str | None, Field(alias="privacyPolicy")] = None + terms_of_service: Annotated[str | None, Field(alias="termsOfService")] = None + website: str | None = None + + +class AppReview(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + status: str + + +class AppScreenshot(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + file_id: Annotated[str | None, Field(alias="fileId")] = None + url: str | None = None + user_prompt: Annotated[str, Field(alias="userPrompt")] + + +class AppSummary(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + description: str | None = None + id: str + install_url: Annotated[str | None, Field(alias="installUrl")] = None + name: str + + +class AppToolApproval(Enum): + auto = "auto" + prompt = "prompt" + approve = "approve" + + +class AppToolConfig(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + approval_mode: AppToolApproval | None = None + enabled: bool | None = None + + +class AppToolsConfig(BaseModel): + pass + model_config = ConfigDict( + populate_by_name=True, + ) + + +class ApprovalsReviewer(Enum): + user = "user" + guardian_subagent = "guardian_subagent" + + +class AppsDefaultConfig(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + destructive_enabled: bool | None = True + enabled: bool | None = True + open_world_enabled: bool | None = True + + +class AppsListParams(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + cursor: Annotated[ + str | None, + Field(description="Opaque pagination cursor returned by a previous call."), + ] = None + force_refetch: Annotated[ + bool | None, + Field( + alias="forceRefetch", + description="When true, bypass app caches and fetch the latest data from sources.", + ), + ] = None + limit: Annotated[ + int | None, + Field( + description="Optional page size; defaults to a reasonable server-side value.", + ge=0, + ), + ] = None + thread_id: Annotated[ + str | None, + Field( + alias="threadId", + description="Optional thread id used to evaluate app feature gating from that thread's config.", + ), + ] = None + + +class AskForApprovalValue(Enum): + untrusted = "untrusted" + on_failure = "on-failure" + on_request = "on-request" + never = "never" + + +class Granular(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + mcp_elicitations: bool + request_permissions: bool | None = False + rules: bool + sandbox_approval: bool + skill_approval: bool | None = False + + +class GranularAskForApproval(BaseModel): + model_config = ConfigDict( + extra="forbid", + populate_by_name=True, + ) + granular: Granular + + +class AskForApproval(RootModel[AskForApprovalValue | GranularAskForApproval]): + model_config = ConfigDict( + populate_by_name=True, + ) + root: AskForApprovalValue | GranularAskForApproval + + +class AuthMode(Enum): + apikey = "apikey" + chatgpt = "chatgpt" + chatgpt_auth_tokens = "chatgptAuthTokens" + + +class ByteRange(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + end: Annotated[int, Field(ge=0)] + start: Annotated[int, Field(ge=0)] + + +class CancelLoginAccountParams(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + login_id: Annotated[str, Field(alias="loginId")] + + +class CancelLoginAccountStatus(Enum): + canceled = "canceled" + not_found = "notFound" + + +class ClientInfo(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + name: str + title: str | None = None + version: str + + +class CodexErrorInfoValue(Enum): + context_window_exceeded = "contextWindowExceeded" + usage_limit_exceeded = "usageLimitExceeded" + server_overloaded = "serverOverloaded" + internal_server_error = "internalServerError" + unauthorized = "unauthorized" + bad_request = "badRequest" + thread_rollback_failed = "threadRollbackFailed" + sandbox_error = "sandboxError" + other = "other" + + +class HttpConnectionFailed(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + http_status_code: Annotated[int | None, Field(alias="httpStatusCode", ge=0)] = None + + +class HttpConnectionFailedCodexErrorInfo(BaseModel): + model_config = ConfigDict( + extra="forbid", + populate_by_name=True, + ) + http_connection_failed: Annotated[HttpConnectionFailed, Field(alias="httpConnectionFailed")] + + +class ResponseStreamConnectionFailed(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + http_status_code: Annotated[int | None, Field(alias="httpStatusCode", ge=0)] = None + + +class ResponseStreamConnectionFailedCodexErrorInfo(BaseModel): + model_config = ConfigDict( + extra="forbid", + populate_by_name=True, + ) + response_stream_connection_failed: Annotated[ + ResponseStreamConnectionFailed, Field(alias="responseStreamConnectionFailed") + ] + + +class ResponseStreamDisconnected(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + http_status_code: Annotated[int | None, Field(alias="httpStatusCode", ge=0)] = None + + +class ResponseStreamDisconnectedCodexErrorInfo(BaseModel): + model_config = ConfigDict( + extra="forbid", + populate_by_name=True, + ) + response_stream_disconnected: Annotated[ + ResponseStreamDisconnected, Field(alias="responseStreamDisconnected") + ] + + +class ResponseTooManyFailedAttempts(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + http_status_code: Annotated[int | None, Field(alias="httpStatusCode", ge=0)] = None + + +class ResponseTooManyFailedAttemptsCodexErrorInfo(BaseModel): + model_config = ConfigDict( + extra="forbid", + populate_by_name=True, + ) + response_too_many_failed_attempts: Annotated[ + ResponseTooManyFailedAttempts, Field(alias="responseTooManyFailedAttempts") + ] + + +class CodexErrorInfo( + RootModel[ + CodexErrorInfoValue + | HttpConnectionFailedCodexErrorInfo + | ResponseStreamConnectionFailedCodexErrorInfo + | ResponseStreamDisconnectedCodexErrorInfo + | ResponseTooManyFailedAttemptsCodexErrorInfo + ] +): + model_config = ConfigDict( + populate_by_name=True, + ) + root: Annotated[ + CodexErrorInfoValue + | HttpConnectionFailedCodexErrorInfo + | ResponseStreamConnectionFailedCodexErrorInfo + | ResponseStreamDisconnectedCodexErrorInfo + | ResponseTooManyFailedAttemptsCodexErrorInfo, + Field( + description="This translation layer make sure that we expose codex error code in camel case.\n\nWhen an upstream HTTP status is available (for example, from the Responses API or a provider), it is forwarded in `httpStatusCode` on the relevant `codexErrorInfo` variant." + ), + ] + + +class CollabAgentStatus(Enum): + pending_init = "pendingInit" + running = "running" + completed = "completed" + errored = "errored" + shutdown = "shutdown" + not_found = "notFound" + + +class CollabAgentTool(Enum): + spawn_agent = "spawnAgent" + send_input = "sendInput" + resume_agent = "resumeAgent" + wait = "wait" + close_agent = "closeAgent" + + +class CollabAgentToolCallStatus(Enum): + in_progress = "inProgress" + completed = "completed" + failed = "failed" + + +class ReadCommandAction(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + command: str + name: str + path: str + type: Annotated[Literal["read"], Field(title="ReadCommandActionType")] + + +class ListFilesCommandAction(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + command: str + path: str | None = None + type: Annotated[Literal["listFiles"], Field(title="ListFilesCommandActionType")] + + +class SearchCommandAction(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + command: str + path: str | None = None + query: str | None = None + type: Annotated[Literal["search"], Field(title="SearchCommandActionType")] + + +class UnknownCommandAction(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + command: str + type: Annotated[Literal["unknown"], Field(title="UnknownCommandActionType")] + + +class CommandAction( + RootModel[ + ReadCommandAction | ListFilesCommandAction | SearchCommandAction | UnknownCommandAction + ] +): + model_config = ConfigDict( + populate_by_name=True, + ) + root: ReadCommandAction | ListFilesCommandAction | SearchCommandAction | UnknownCommandAction + + +class CommandExecOutputStream(Enum): + stdout = "stdout" + stderr = "stderr" + + +class CommandExecResizeResponse(BaseModel): + pass + model_config = ConfigDict( + populate_by_name=True, + ) + + +class CommandExecResponse(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + exit_code: Annotated[int, Field(alias="exitCode", description="Process exit code.")] + stderr: Annotated[ + str, + Field( + description="Buffered stderr capture.\n\nEmpty when stderr was streamed via `command/exec/outputDelta`." + ), + ] + stdout: Annotated[ + str, + Field( + description="Buffered stdout capture.\n\nEmpty when stdout was streamed via `command/exec/outputDelta`." + ), + ] + + +class CommandExecTerminalSize(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + cols: Annotated[int, Field(description="Terminal width in character cells.", ge=0)] + rows: Annotated[int, Field(description="Terminal height in character cells.", ge=0)] + + +class CommandExecTerminateParams(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + process_id: Annotated[ + str, + Field( + alias="processId", + description="Client-supplied, connection-scoped `processId` from the original `command/exec` request.", + ), + ] + + +class CommandExecTerminateResponse(BaseModel): + pass + model_config = ConfigDict( + populate_by_name=True, + ) + + +class CommandExecWriteParams(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + close_stdin: Annotated[ + bool | None, + Field( + alias="closeStdin", + description="Close stdin after writing `deltaBase64`, if present.", + ), + ] = None + delta_base64: Annotated[ + str | None, + Field( + alias="deltaBase64", + description="Optional base64-encoded stdin bytes to write.", + ), + ] = None + process_id: Annotated[ + str, + Field( + alias="processId", + description="Client-supplied, connection-scoped `processId` from the original `command/exec` request.", + ), + ] + + +class CommandExecWriteResponse(BaseModel): + pass + model_config = ConfigDict( + populate_by_name=True, + ) + + +class CommandExecutionOutputDeltaNotification(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + delta: str + item_id: Annotated[str, Field(alias="itemId")] + thread_id: Annotated[str, Field(alias="threadId")] + turn_id: Annotated[str, Field(alias="turnId")] + + +class CommandExecutionStatus(Enum): + in_progress = "inProgress" + completed = "completed" + failed = "failed" + declined = "declined" + + +class MdmConfigLayerSource(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + domain: str + key: str + type: Annotated[Literal["mdm"], Field(title="MdmConfigLayerSourceType")] + + +class SystemConfigLayerSource(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + file: Annotated[ + AbsolutePathBuf, + Field( + description="This is the path to the system config.toml file, though it is not guaranteed to exist." + ), + ] + type: Annotated[Literal["system"], Field(title="SystemConfigLayerSourceType")] + + +class UserConfigLayerSource(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + file: Annotated[ + AbsolutePathBuf, + Field( + description="This is the path to the user's config.toml file, though it is not guaranteed to exist." + ), + ] + type: Annotated[Literal["user"], Field(title="UserConfigLayerSourceType")] + + +class ProjectConfigLayerSource(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + dot_codex_folder: Annotated[AbsolutePathBuf, Field(alias="dotCodexFolder")] + type: Annotated[Literal["project"], Field(title="ProjectConfigLayerSourceType")] + + +class SessionFlagsConfigLayerSource(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + type: Annotated[Literal["sessionFlags"], Field(title="SessionFlagsConfigLayerSourceType")] + + +class LegacyManagedConfigTomlFromFileConfigLayerSource(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + file: AbsolutePathBuf + type: Annotated[ + Literal["legacyManagedConfigTomlFromFile"], + Field(title="LegacyManagedConfigTomlFromFileConfigLayerSourceType"), + ] + + +class LegacyManagedConfigTomlFromMdmConfigLayerSource(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + type: Annotated[ + Literal["legacyManagedConfigTomlFromMdm"], + Field(title="LegacyManagedConfigTomlFromMdmConfigLayerSourceType"), + ] + + +class ConfigLayerSource( + RootModel[ + MdmConfigLayerSource + | SystemConfigLayerSource + | UserConfigLayerSource + | ProjectConfigLayerSource + | SessionFlagsConfigLayerSource + | LegacyManagedConfigTomlFromFileConfigLayerSource + | LegacyManagedConfigTomlFromMdmConfigLayerSource + ] +): + model_config = ConfigDict( + populate_by_name=True, + ) + root: ( + MdmConfigLayerSource + | SystemConfigLayerSource + | UserConfigLayerSource + | ProjectConfigLayerSource + | SessionFlagsConfigLayerSource + | LegacyManagedConfigTomlFromFileConfigLayerSource + | LegacyManagedConfigTomlFromMdmConfigLayerSource + ) + + +class ConfigReadParams(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + cwd: Annotated[ + str | None, + Field( + description="Optional working directory to resolve project config layers. If specified, return the effective config as seen from that directory (i.e., including any project layers between `cwd` and the project/repo root)." + ), + ] = None + include_layers: Annotated[bool | None, Field(alias="includeLayers")] = False + + +class InputTextContentItem(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + text: str + type: Annotated[Literal["input_text"], Field(title="InputTextContentItemType")] + + +class InputImageContentItem(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + image_url: str + type: Annotated[Literal["input_image"], Field(title="InputImageContentItemType")] + + +class OutputTextContentItem(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + text: str + type: Annotated[Literal["output_text"], Field(title="OutputTextContentItemType")] + + +class ContentItem(RootModel[InputTextContentItem | InputImageContentItem | OutputTextContentItem]): + model_config = ConfigDict( + populate_by_name=True, + ) + root: InputTextContentItem | InputImageContentItem | OutputTextContentItem + + +class ContextCompactedNotification(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + thread_id: Annotated[str, Field(alias="threadId")] + turn_id: Annotated[str, Field(alias="turnId")] + + +class CreditsSnapshot(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + balance: str | None = None + has_credits: Annotated[bool, Field(alias="hasCredits")] + unlimited: bool + + +class DeprecationNoticeNotification(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + details: Annotated[ + str | None, + Field(description="Optional extra guidance, such as migration steps or rationale."), + ] = None + summary: Annotated[str, Field(description="Concise summary of what is deprecated.")] + + +class InputTextDynamicToolCallOutputContentItem(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + text: str + type: Annotated[ + Literal["inputText"], + Field(title="InputTextDynamicToolCallOutputContentItemType"), + ] + + +class InputImageDynamicToolCallOutputContentItem(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + image_url: Annotated[str, Field(alias="imageUrl")] + type: Annotated[ + Literal["inputImage"], + Field(title="InputImageDynamicToolCallOutputContentItemType"), + ] + + +class DynamicToolCallOutputContentItem( + RootModel[ + InputTextDynamicToolCallOutputContentItem | InputImageDynamicToolCallOutputContentItem + ] +): + model_config = ConfigDict( + populate_by_name=True, + ) + root: InputTextDynamicToolCallOutputContentItem | InputImageDynamicToolCallOutputContentItem + + +class DynamicToolCallStatus(Enum): + in_progress = "inProgress" + completed = "completed" + failed = "failed" + + +class DynamicToolSpec(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + description: str + input_schema: Annotated[Any, Field(alias="inputSchema")] + name: str + + +class ExperimentalFeatureListParams(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + cursor: Annotated[ + str | None, + Field(description="Opaque pagination cursor returned by a previous call."), + ] = None + limit: Annotated[ + int | None, + Field( + description="Optional page size; defaults to a reasonable server-side value.", + ge=0, + ), + ] = None + + +class ExperimentalFeatureStage(Enum): + beta = "beta" + under_development = "underDevelopment" + stable = "stable" + deprecated = "deprecated" + removed = "removed" + + +class ExternalAgentConfigDetectParams(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + cwds: Annotated[ + list[str] | None, + Field(description="Zero or more working directories to include for repo-scoped detection."), + ] = None + include_home: Annotated[ + bool | None, + Field( + alias="includeHome", + description="If true, include detection under the user's home (~/.claude, ~/.codex, etc.).", + ), + ] = None + + +class ExternalAgentConfigImportResponse(BaseModel): + pass + model_config = ConfigDict( + populate_by_name=True, + ) + + +class ExternalAgentConfigMigrationItemType(Enum): + agents_md = "AGENTS_MD" + config = "CONFIG" + skills = "SKILLS" + mcp_server_config = "MCP_SERVER_CONFIG" + + +class FeedbackUploadParams(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + classification: str + extra_log_files: Annotated[list[str] | None, Field(alias="extraLogFiles")] = None + include_logs: Annotated[bool, Field(alias="includeLogs")] + reason: str | None = None + thread_id: Annotated[str | None, Field(alias="threadId")] = None + + +class FeedbackUploadResponse(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + thread_id: Annotated[str, Field(alias="threadId")] + + +class FileChangeOutputDeltaNotification(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + delta: str + item_id: Annotated[str, Field(alias="itemId")] + thread_id: Annotated[str, Field(alias="threadId")] + turn_id: Annotated[str, Field(alias="turnId")] + + +class ForcedLoginMethod(Enum): + chatgpt = "chatgpt" + api = "api" + + +class FsCopyParams(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + destination_path: Annotated[ + AbsolutePathBuf, + Field(alias="destinationPath", description="Absolute destination path."), + ] + recursive: Annotated[ + bool | None, + Field(description="Required for directory copies; ignored for file copies."), + ] = None + source_path: Annotated[ + AbsolutePathBuf, Field(alias="sourcePath", description="Absolute source path.") + ] + + +class FsCopyResponse(BaseModel): + pass + model_config = ConfigDict( + populate_by_name=True, + ) + + +class FsCreateDirectoryParams(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + path: Annotated[AbsolutePathBuf, Field(description="Absolute directory path to create.")] + recursive: Annotated[ + bool | None, + Field(description="Whether parent directories should also be created. Defaults to `true`."), + ] = None + + +class FsCreateDirectoryResponse(BaseModel): + pass + model_config = ConfigDict( + populate_by_name=True, + ) + + +class FsGetMetadataParams(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + path: Annotated[AbsolutePathBuf, Field(description="Absolute path to inspect.")] + + +class FsGetMetadataResponse(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + created_at_ms: Annotated[ + int, + Field( + alias="createdAtMs", + description="File creation time in Unix milliseconds when available, otherwise `0`.", + ), + ] + is_directory: Annotated[ + bool, + Field( + alias="isDirectory", + description="Whether the path currently resolves to a directory.", + ), + ] + is_file: Annotated[ + bool, + Field( + alias="isFile", + description="Whether the path currently resolves to a regular file.", + ), + ] + modified_at_ms: Annotated[ + int, + Field( + alias="modifiedAtMs", + description="File modification time in Unix milliseconds when available, otherwise `0`.", + ), + ] + + +class FsReadDirectoryEntry(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + file_name: Annotated[ + str, + Field( + alias="fileName", + description="Direct child entry name only, not an absolute or relative path.", + ), + ] + is_directory: Annotated[ + bool, + Field( + alias="isDirectory", + description="Whether this entry resolves to a directory.", + ), + ] + is_file: Annotated[ + bool, + Field(alias="isFile", description="Whether this entry resolves to a regular file."), + ] + + +class FsReadDirectoryParams(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + path: Annotated[AbsolutePathBuf, Field(description="Absolute directory path to read.")] + + +class FsReadDirectoryResponse(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + entries: Annotated[ + list[FsReadDirectoryEntry], + Field(description="Direct child entries in the requested directory."), + ] + + +class FsReadFileParams(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + path: Annotated[AbsolutePathBuf, Field(description="Absolute path to read.")] + + +class FsReadFileResponse(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + data_base64: Annotated[ + str, Field(alias="dataBase64", description="File contents encoded as base64.") + ] + + +class FsRemoveParams(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + force: Annotated[ + bool | None, + Field(description="Whether missing paths should be ignored. Defaults to `true`."), + ] = None + path: Annotated[AbsolutePathBuf, Field(description="Absolute path to remove.")] + recursive: Annotated[ + bool | None, + Field(description="Whether directory removal should recurse. Defaults to `true`."), + ] = None + + +class FsRemoveResponse(BaseModel): + pass + model_config = ConfigDict( + populate_by_name=True, + ) + + +class FsWriteFileParams(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + data_base64: Annotated[ + str, Field(alias="dataBase64", description="File contents encoded as base64.") + ] + path: Annotated[AbsolutePathBuf, Field(description="Absolute path to write.")] + + +class FsWriteFileResponse(BaseModel): + pass + model_config = ConfigDict( + populate_by_name=True, + ) + + +class InputTextFunctionCallOutputContentItem(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + text: str + type: Annotated[ + Literal["input_text"], Field(title="InputTextFunctionCallOutputContentItemType") + ] + + +class FuzzyFileSearchParams(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + cancellation_token: Annotated[str | None, Field(alias="cancellationToken")] = None + query: str + roots: list[str] + + +class Indice(RootModel[int]): + model_config = ConfigDict( + populate_by_name=True, + ) + root: Annotated[int, Field(ge=0)] + + +class FuzzyFileSearchResult(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + file_name: str + indices: list[Indice] | None = None + path: str + root: str + score: Annotated[int, Field(ge=0)] + + +class FuzzyFileSearchSessionCompletedNotification(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + session_id: Annotated[str, Field(alias="sessionId")] + + +class FuzzyFileSearchSessionUpdatedNotification(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + files: list[FuzzyFileSearchResult] + query: str + session_id: Annotated[str, Field(alias="sessionId")] + + +class GetAccountParams(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + refresh_token: Annotated[ + bool | None, + Field( + alias="refreshToken", + description="When `true`, requests a proactive token refresh before returning.\n\nIn managed auth mode this triggers the normal refresh-token flow. In external auth mode this flag is ignored. Clients should refresh tokens themselves and call `account/login/start` with `chatgptAuthTokens`.", + ), + ] = False + + +class GhostCommit(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + id: str + parent: str | None = None + preexisting_untracked_dirs: list[str] + preexisting_untracked_files: list[str] + + +class GitInfo(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + branch: str | None = None + origin_url: Annotated[str | None, Field(alias="originUrl")] = None + sha: str | None = None + + +class GuardianApprovalReviewStatus(Enum): + in_progress = "inProgress" + approved = "approved" + denied = "denied" + aborted = "aborted" + + +class GuardianRiskLevel(Enum): + low = "low" + medium = "medium" + high = "high" + + +class HazelnutScope(Enum): + example = "example" + workspace_shared = "workspace-shared" + all_shared = "all-shared" + personal = "personal" + + +class HookEventName(Enum): + session_start = "sessionStart" + stop = "stop" + + +class HookExecutionMode(Enum): + sync = "sync" + async_ = "async" + + +class HookHandlerType(Enum): + command = "command" + prompt = "prompt" + agent = "agent" + + +class HookOutputEntryKind(Enum): + warning = "warning" + stop = "stop" + feedback = "feedback" + context = "context" + error = "error" + + +class HookRunStatus(Enum): + running = "running" + completed = "completed" + failed = "failed" + blocked = "blocked" + stopped = "stopped" + + +class HookScope(Enum): + thread = "thread" + turn = "turn" + + +class ImageDetail(Enum): + auto = "auto" + low = "low" + high = "high" + original = "original" + + +class InitializeCapabilities(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + experimental_api: Annotated[ + bool | None, + Field( + alias="experimentalApi", + description="Opt into receiving experimental API methods and fields.", + ), + ] = False + opt_out_notification_methods: Annotated[ + list[str] | None, + Field( + alias="optOutNotificationMethods", + description="Exact notification method names that should be suppressed for this connection (for example `thread/started`).", + ), + ] = None + + +class InitializeParams(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + capabilities: InitializeCapabilities | None = None + client_info: Annotated[ClientInfo, Field(alias="clientInfo")] + + +class InputModality(Enum): + text = "text" + image = "image" + + +class ListMcpServerStatusParams(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + cursor: Annotated[ + str | None, + Field(description="Opaque pagination cursor returned by a previous call."), + ] = None + limit: Annotated[ + int | None, + Field(description="Optional page size; defaults to a server-defined value.", ge=0), + ] = None + + +class ExecLocalShellAction(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + command: list[str] + env: dict[str, Any] | None = None + timeout_ms: Annotated[int | None, Field(ge=0)] = None + type: Annotated[Literal["exec"], Field(title="ExecLocalShellActionType")] + user: str | None = None + working_directory: str | None = None + + +class LocalShellAction(RootModel[ExecLocalShellAction]): + model_config = ConfigDict( + populate_by_name=True, + ) + root: ExecLocalShellAction + + +class LocalShellStatus(Enum): + completed = "completed" + in_progress = "in_progress" + incomplete = "incomplete" + + +class ApiKeyLoginAccountParams(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + api_key: Annotated[str, Field(alias="apiKey")] + type: Annotated[Literal["apiKey"], Field(title="ApiKeyv2::LoginAccountParamsType")] + + +class ChatgptLoginAccountParams(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + type: Annotated[Literal["chatgpt"], Field(title="Chatgptv2::LoginAccountParamsType")] + + +class ChatgptAuthTokensLoginAccountParams(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + access_token: Annotated[ + str, + Field( + alias="accessToken", + description="Access token (JWT) supplied by the client. This token is used for backend API requests and email extraction.", + ), + ] + chatgpt_account_id: Annotated[ + str, + Field( + alias="chatgptAccountId", + description="Workspace/account identifier supplied by the client.", + ), + ] + chatgpt_plan_type: Annotated[ + str | None, + Field( + alias="chatgptPlanType", + description="Optional plan type supplied by the client.\n\nWhen `null`, Codex attempts to derive the plan type from access-token claims. If unavailable, the plan defaults to `unknown`.", + ), + ] = None + type: Annotated[ + Literal["chatgptAuthTokens"], + Field(title="ChatgptAuthTokensv2::LoginAccountParamsType"), + ] + + +class LoginAccountParams( + RootModel[ + ApiKeyLoginAccountParams | ChatgptLoginAccountParams | ChatgptAuthTokensLoginAccountParams + ] +): + model_config = ConfigDict( + populate_by_name=True, + ) + root: Annotated[ + ApiKeyLoginAccountParams | ChatgptLoginAccountParams | ChatgptAuthTokensLoginAccountParams, + Field(title="LoginAccountParams"), + ] + + +class ApiKeyLoginAccountResponse(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + type: Annotated[Literal["apiKey"], Field(title="ApiKeyv2::LoginAccountResponseType")] + + +class ChatgptLoginAccountResponse(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + auth_url: Annotated[ + str, + Field( + alias="authUrl", + description="URL the client should open in a browser to initiate the OAuth flow.", + ), + ] + login_id: Annotated[str, Field(alias="loginId")] + type: Annotated[Literal["chatgpt"], Field(title="Chatgptv2::LoginAccountResponseType")] + + +class ChatgptAuthTokensLoginAccountResponse(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + type: Annotated[ + Literal["chatgptAuthTokens"], + Field(title="ChatgptAuthTokensv2::LoginAccountResponseType"), + ] + + +class LoginAccountResponse( + RootModel[ + ApiKeyLoginAccountResponse + | ChatgptLoginAccountResponse + | ChatgptAuthTokensLoginAccountResponse + ] +): + model_config = ConfigDict( + populate_by_name=True, + ) + root: Annotated[ + ApiKeyLoginAccountResponse + | ChatgptLoginAccountResponse + | ChatgptAuthTokensLoginAccountResponse, + Field(title="LoginAccountResponse"), + ] + + +class LogoutAccountResponse(BaseModel): + pass + model_config = ConfigDict( + populate_by_name=True, + ) + + +class McpAuthStatus(Enum): + unsupported = "unsupported" + not_logged_in = "notLoggedIn" + bearer_token = "bearerToken" + o_auth = "oAuth" + + +class McpServerOauthLoginCompletedNotification(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + error: str | None = None + name: str + success: bool + + +class McpServerOauthLoginParams(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + name: str + scopes: list[str] | None = None + timeout_secs: Annotated[int | None, Field(alias="timeoutSecs")] = None + + +class McpServerOauthLoginResponse(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + authorization_url: Annotated[str, Field(alias="authorizationUrl")] + + +class McpServerRefreshResponse(BaseModel): + pass + model_config = ConfigDict( + populate_by_name=True, + ) + + +class McpToolCallError(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + message: str + + +class McpToolCallProgressNotification(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + item_id: Annotated[str, Field(alias="itemId")] + message: str + thread_id: Annotated[str, Field(alias="threadId")] + turn_id: Annotated[str, Field(alias="turnId")] + + +class McpToolCallResult(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + content: list + structured_content: Annotated[Any | None, Field(alias="structuredContent")] = None + + +class McpToolCallStatus(Enum): + in_progress = "inProgress" + completed = "completed" + failed = "failed" + + +class MergeStrategy(Enum): + replace = "replace" + upsert = "upsert" + + +class MessagePhase(Enum): + commentary = "commentary" + final_answer = "final_answer" + + +class ModeKind(Enum): + plan = "plan" + default = "default" + + +class ModelAvailabilityNux(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + message: str + + +class ModelListParams(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + cursor: Annotated[ + str | None, + Field(description="Opaque pagination cursor returned by a previous call."), + ] = None + include_hidden: Annotated[ + bool | None, + Field( + alias="includeHidden", + description="When true, include models that are hidden from the default picker list.", + ), + ] = None + limit: Annotated[ + int | None, + Field( + description="Optional page size; defaults to a reasonable server-side value.", + ge=0, + ), + ] = None + + +class ModelRerouteReason(RootModel[Literal["highRiskCyberActivity"]]): + model_config = ConfigDict( + populate_by_name=True, + ) + root: Literal["highRiskCyberActivity"] + + +class ModelReroutedNotification(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + from_model: Annotated[str, Field(alias="fromModel")] + reason: ModelRerouteReason + thread_id: Annotated[str, Field(alias="threadId")] + to_model: Annotated[str, Field(alias="toModel")] + turn_id: Annotated[str, Field(alias="turnId")] + + +class ModelUpgradeInfo(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + migration_markdown: Annotated[str | None, Field(alias="migrationMarkdown")] = None + model: str + model_link: Annotated[str | None, Field(alias="modelLink")] = None + upgrade_copy: Annotated[str | None, Field(alias="upgradeCopy")] = None + + +class NetworkAccess(Enum): + restricted = "restricted" + enabled = "enabled" + + +class NetworkRequirements(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + allow_local_binding: Annotated[bool | None, Field(alias="allowLocalBinding")] = None + allow_unix_sockets: Annotated[list[str] | None, Field(alias="allowUnixSockets")] = None + allow_upstream_proxy: Annotated[bool | None, Field(alias="allowUpstreamProxy")] = None + allowed_domains: Annotated[list[str] | None, Field(alias="allowedDomains")] = None + dangerously_allow_all_unix_sockets: Annotated[ + bool | None, Field(alias="dangerouslyAllowAllUnixSockets") + ] = None + dangerously_allow_non_loopback_proxy: Annotated[ + bool | None, Field(alias="dangerouslyAllowNonLoopbackProxy") + ] = None + denied_domains: Annotated[list[str] | None, Field(alias="deniedDomains")] = None + enabled: bool | None = None + http_port: Annotated[int | None, Field(alias="httpPort", ge=0)] = None + socks_port: Annotated[int | None, Field(alias="socksPort", ge=0)] = None + + +class PatchApplyStatus(Enum): + in_progress = "inProgress" + completed = "completed" + failed = "failed" + declined = "declined" + + +class AddPatchChangeKind(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + type: Annotated[Literal["add"], Field(title="AddPatchChangeKindType")] + + +class DeletePatchChangeKind(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + type: Annotated[Literal["delete"], Field(title="DeletePatchChangeKindType")] + + +class UpdatePatchChangeKind(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + move_path: str | None = None + type: Annotated[Literal["update"], Field(title="UpdatePatchChangeKindType")] + + +class PatchChangeKind( + RootModel[AddPatchChangeKind | DeletePatchChangeKind | UpdatePatchChangeKind] +): + model_config = ConfigDict( + populate_by_name=True, + ) + root: AddPatchChangeKind | DeletePatchChangeKind | UpdatePatchChangeKind + + +class Personality(Enum): + none = "none" + friendly = "friendly" + pragmatic = "pragmatic" + + +class PlanDeltaNotification(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + delta: str + item_id: Annotated[str, Field(alias="itemId")] + thread_id: Annotated[str, Field(alias="threadId")] + turn_id: Annotated[str, Field(alias="turnId")] + + +class PlanType(Enum): + free = "free" + go = "go" + plus = "plus" + pro = "pro" + team = "team" + business = "business" + enterprise = "enterprise" + edu = "edu" + unknown = "unknown" + + +class PluginAuthPolicy(Enum): + on_install = "ON_INSTALL" + on_use = "ON_USE" + + +class PluginInstallParams(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + marketplace_path: Annotated[AbsolutePathBuf, Field(alias="marketplacePath")] + plugin_name: Annotated[str, Field(alias="pluginName")] + + +class PluginInstallPolicy(Enum): + not_available = "NOT_AVAILABLE" + available = "AVAILABLE" + installed_by_default = "INSTALLED_BY_DEFAULT" + + +class PluginInstallResponse(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + apps_needing_auth: Annotated[list[AppSummary], Field(alias="appsNeedingAuth")] + auth_policy: Annotated[PluginAuthPolicy, Field(alias="authPolicy")] + + +class PluginInterface(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + brand_color: Annotated[str | None, Field(alias="brandColor")] = None + capabilities: list[str] + category: str | None = None + composer_icon: Annotated[AbsolutePathBuf | None, Field(alias="composerIcon")] = None + default_prompt: Annotated[str | None, Field(alias="defaultPrompt")] = None + developer_name: Annotated[str | None, Field(alias="developerName")] = None + display_name: Annotated[str | None, Field(alias="displayName")] = None + logo: AbsolutePathBuf | None = None + long_description: Annotated[str | None, Field(alias="longDescription")] = None + privacy_policy_url: Annotated[str | None, Field(alias="privacyPolicyUrl")] = None + screenshots: list[AbsolutePathBuf] + short_description: Annotated[str | None, Field(alias="shortDescription")] = None + terms_of_service_url: Annotated[str | None, Field(alias="termsOfServiceUrl")] = None + website_url: Annotated[str | None, Field(alias="websiteUrl")] = None + + +class PluginListParams(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + cwds: Annotated[ + list[AbsolutePathBuf] | None, + Field( + description="Optional working directories used to discover repo marketplaces. When omitted, only home-scoped marketplaces and the official curated marketplace are considered." + ), + ] = None + force_remote_sync: Annotated[ + bool | None, + Field( + alias="forceRemoteSync", + description="When true, reconcile the official curated marketplace against the remote plugin state before listing marketplaces.", + ), + ] = None + + +class PluginReadParams(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + marketplace_path: Annotated[AbsolutePathBuf, Field(alias="marketplacePath")] + plugin_name: Annotated[str, Field(alias="pluginName")] + + +class LocalPluginSource(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + path: AbsolutePathBuf + type: Annotated[Literal["local"], Field(title="LocalPluginSourceType")] + + +class PluginSource(RootModel[LocalPluginSource]): + model_config = ConfigDict( + populate_by_name=True, + ) + root: LocalPluginSource + + +class PluginSummary(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + auth_policy: Annotated[PluginAuthPolicy, Field(alias="authPolicy")] + enabled: bool + id: str + install_policy: Annotated[PluginInstallPolicy, Field(alias="installPolicy")] + installed: bool + interface: PluginInterface | None = None + name: str + source: PluginSource + + +class PluginUninstallParams(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + plugin_id: Annotated[str, Field(alias="pluginId")] + + +class PluginUninstallResponse(BaseModel): + pass + model_config = ConfigDict( + populate_by_name=True, + ) + + +class ProductSurface(Enum): + chatgpt = "chatgpt" + codex = "codex" + api = "api" + atlas = "atlas" + + +class RateLimitWindow(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + resets_at: Annotated[int | None, Field(alias="resetsAt")] = None + used_percent: Annotated[int, Field(alias="usedPercent")] + window_duration_mins: Annotated[int | None, Field(alias="windowDurationMins")] = None + + +class RestrictedReadOnlyAccess(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + include_platform_defaults: Annotated[bool | None, Field(alias="includePlatformDefaults")] = True + readable_roots: Annotated[list[AbsolutePathBuf] | None, Field(alias="readableRoots")] = [] + type: Annotated[Literal["restricted"], Field(title="RestrictedReadOnlyAccessType")] + + +class FullAccessReadOnlyAccess(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + type: Annotated[Literal["fullAccess"], Field(title="FullAccessReadOnlyAccessType")] + + +class ReadOnlyAccess(RootModel[RestrictedReadOnlyAccess | FullAccessReadOnlyAccess]): + model_config = ConfigDict( + populate_by_name=True, + ) + root: RestrictedReadOnlyAccess | FullAccessReadOnlyAccess + + +class ReasoningEffort(Enum): + none = "none" + minimal = "minimal" + low = "low" + medium = "medium" + high = "high" + xhigh = "xhigh" + + +class ReasoningEffortOption(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + description: str + reasoning_effort: Annotated[ReasoningEffort, Field(alias="reasoningEffort")] + + +class ReasoningTextReasoningItemContent(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + text: str + type: Annotated[Literal["reasoning_text"], Field(title="ReasoningTextReasoningItemContentType")] + + +class TextReasoningItemContent(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + text: str + type: Annotated[Literal["text"], Field(title="TextReasoningItemContentType")] + + +class ReasoningItemContent(RootModel[ReasoningTextReasoningItemContent | TextReasoningItemContent]): + model_config = ConfigDict( + populate_by_name=True, + ) + root: ReasoningTextReasoningItemContent | TextReasoningItemContent + + +class SummaryTextReasoningItemReasoningSummary(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + text: str + type: Annotated[ + Literal["summary_text"], + Field(title="SummaryTextReasoningItemReasoningSummaryType"), + ] + + +class ReasoningItemReasoningSummary(RootModel[SummaryTextReasoningItemReasoningSummary]): + model_config = ConfigDict( + populate_by_name=True, + ) + root: SummaryTextReasoningItemReasoningSummary + + +class ReasoningSummaryValue(Enum): + auto = "auto" + concise = "concise" + detailed = "detailed" + + +class ReasoningSummary(RootModel[ReasoningSummaryValue | Literal["none"]]): + model_config = ConfigDict( + populate_by_name=True, + ) + root: Annotated[ + ReasoningSummaryValue | Literal["none"], + Field( + description="A summary of the reasoning performed by the model. This can be useful for debugging and understanding the model's reasoning process. See https://platform.openai.com/docs/guides/reasoning?api-mode=responses#reasoning-summaries" + ), + ] + + +class ReasoningSummaryPartAddedNotification(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + item_id: Annotated[str, Field(alias="itemId")] + summary_index: Annotated[int, Field(alias="summaryIndex")] + thread_id: Annotated[str, Field(alias="threadId")] + turn_id: Annotated[str, Field(alias="turnId")] + + +class ReasoningSummaryTextDeltaNotification(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + delta: str + item_id: Annotated[str, Field(alias="itemId")] + summary_index: Annotated[int, Field(alias="summaryIndex")] + thread_id: Annotated[str, Field(alias="threadId")] + turn_id: Annotated[str, Field(alias="turnId")] + + +class ReasoningTextDeltaNotification(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + content_index: Annotated[int, Field(alias="contentIndex")] + delta: str + item_id: Annotated[str, Field(alias="itemId")] + thread_id: Annotated[str, Field(alias="threadId")] + turn_id: Annotated[str, Field(alias="turnId")] + + +class RemoteSkillSummary(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + description: str + id: str + name: str + + +class RequestId(RootModel[str | int]): + model_config = ConfigDict( + populate_by_name=True, + ) + root: str | int + + +class ResidencyRequirement(RootModel[Literal["us"]]): + model_config = ConfigDict( + populate_by_name=True, + ) + root: Literal["us"] + + +class Resource(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + field_meta: Annotated[Any | None, Field(alias="_meta")] = None + annotations: Any | None = None + description: str | None = None + icons: list | None = None + mime_type: Annotated[str | None, Field(alias="mimeType")] = None + name: str + size: int | None = None + title: str | None = None + uri: str + + +class ResourceTemplate(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + annotations: Any | None = None + description: str | None = None + mime_type: Annotated[str | None, Field(alias="mimeType")] = None + name: str + title: str | None = None + uri_template: Annotated[str, Field(alias="uriTemplate")] + + +class MessageResponseItem(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + content: list[ContentItem] + end_turn: bool | None = None + id: str | None = None + phase: MessagePhase | None = None + role: str + type: Annotated[Literal["message"], Field(title="MessageResponseItemType")] + + +class ReasoningResponseItem(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + content: list[ReasoningItemContent] | None = None + encrypted_content: str | None = None + id: str + summary: list[ReasoningItemReasoningSummary] + type: Annotated[Literal["reasoning"], Field(title="ReasoningResponseItemType")] + + +class LocalShellCallResponseItem(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + action: LocalShellAction + call_id: Annotated[str | None, Field(description="Set when using the Responses API.")] = None + id: Annotated[ + str | None, + Field(description="Legacy id field retained for compatibility with older payloads."), + ] = None + status: LocalShellStatus + type: Annotated[Literal["local_shell_call"], Field(title="LocalShellCallResponseItemType")] + + +class FunctionCallResponseItem(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + arguments: str + call_id: str + id: str | None = None + name: str + namespace: str | None = None + type: Annotated[Literal["function_call"], Field(title="FunctionCallResponseItemType")] + + +class ToolSearchCallResponseItem(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + arguments: Any + call_id: str | None = None + execution: str + id: str | None = None + status: str | None = None + type: Annotated[Literal["tool_search_call"], Field(title="ToolSearchCallResponseItemType")] + + +class CustomToolCallResponseItem(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + call_id: str + id: str | None = None + input: str + name: str + status: str | None = None + type: Annotated[Literal["custom_tool_call"], Field(title="CustomToolCallResponseItemType")] + + +class ToolSearchOutputResponseItem(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + call_id: str | None = None + execution: str + status: str + tools: list + type: Annotated[Literal["tool_search_output"], Field(title="ToolSearchOutputResponseItemType")] + + +class ImageGenerationCallResponseItem(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + id: str + result: str + revised_prompt: str | None = None + status: str + type: Annotated[ + Literal["image_generation_call"], + Field(title="ImageGenerationCallResponseItemType"), + ] + + +class GhostSnapshotResponseItem(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + ghost_commit: GhostCommit + type: Annotated[Literal["ghost_snapshot"], Field(title="GhostSnapshotResponseItemType")] + + +class CompactionResponseItem(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + encrypted_content: str + type: Annotated[Literal["compaction"], Field(title="CompactionResponseItemType")] + + +class OtherResponseItem(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + type: Annotated[Literal["other"], Field(title="OtherResponseItemType")] + + +class SearchResponsesApiWebSearchAction(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + queries: list[str] | None = None + query: str | None = None + type: Annotated[Literal["search"], Field(title="SearchResponsesApiWebSearchActionType")] + + +class OpenPageResponsesApiWebSearchAction(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + type: Annotated[Literal["open_page"], Field(title="OpenPageResponsesApiWebSearchActionType")] + url: str | None = None + + +class FindInPageResponsesApiWebSearchAction(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + pattern: str | None = None + type: Annotated[ + Literal["find_in_page"], + Field(title="FindInPageResponsesApiWebSearchActionType"), + ] + url: str | None = None + + +class OtherResponsesApiWebSearchAction(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + type: Annotated[Literal["other"], Field(title="OtherResponsesApiWebSearchActionType")] + + +class ResponsesApiWebSearchAction( + RootModel[ + SearchResponsesApiWebSearchAction + | OpenPageResponsesApiWebSearchAction + | FindInPageResponsesApiWebSearchAction + | OtherResponsesApiWebSearchAction + ] +): + model_config = ConfigDict( + populate_by_name=True, + ) + root: ( + SearchResponsesApiWebSearchAction + | OpenPageResponsesApiWebSearchAction + | FindInPageResponsesApiWebSearchAction + | OtherResponsesApiWebSearchAction + ) + + +class ReviewDelivery(Enum): + inline = "inline" + detached = "detached" + + +class UncommittedChangesReviewTarget(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + type: Annotated[ + Literal["uncommittedChanges"], Field(title="UncommittedChangesReviewTargetType") + ] + + +class BaseBranchReviewTarget(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + branch: str + type: Annotated[Literal["baseBranch"], Field(title="BaseBranchReviewTargetType")] + + +class CommitReviewTarget(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + sha: str + title: Annotated[ + str | None, + Field(description="Optional human-readable label (e.g., commit subject) for UIs."), + ] = None + type: Annotated[Literal["commit"], Field(title="CommitReviewTargetType")] + + +class CustomReviewTarget(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + instructions: str + type: Annotated[Literal["custom"], Field(title="CustomReviewTargetType")] + + +class ReviewTarget( + RootModel[ + UncommittedChangesReviewTarget + | BaseBranchReviewTarget + | CommitReviewTarget + | CustomReviewTarget + ] +): + model_config = ConfigDict( + populate_by_name=True, + ) + root: ( + UncommittedChangesReviewTarget + | BaseBranchReviewTarget + | CommitReviewTarget + | CustomReviewTarget + ) + + +class SandboxMode(Enum): + read_only = "read-only" + workspace_write = "workspace-write" + danger_full_access = "danger-full-access" + + +class DangerFullAccessSandboxPolicy(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + type: Annotated[Literal["dangerFullAccess"], Field(title="DangerFullAccessSandboxPolicyType")] + + +class ReadOnlySandboxPolicy(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + access: Annotated[ReadOnlyAccess | None, Field()] = {"type": "fullAccess"} + network_access: Annotated[bool | None, Field(alias="networkAccess")] = False + type: Annotated[Literal["readOnly"], Field(title="ReadOnlySandboxPolicyType")] + + +class ExternalSandboxSandboxPolicy(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + network_access: Annotated[NetworkAccess | None, Field(alias="networkAccess")] = "restricted" + type: Annotated[Literal["externalSandbox"], Field(title="ExternalSandboxSandboxPolicyType")] + + +class WorkspaceWriteSandboxPolicy(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + exclude_slash_tmp: Annotated[bool | None, Field(alias="excludeSlashTmp")] = False + exclude_tmpdir_env_var: Annotated[bool | None, Field(alias="excludeTmpdirEnvVar")] = False + network_access: Annotated[bool | None, Field(alias="networkAccess")] = False + read_only_access: Annotated[ReadOnlyAccess | None, Field(alias="readOnlyAccess")] = { + "type": "fullAccess" + } + type: Annotated[Literal["workspaceWrite"], Field(title="WorkspaceWriteSandboxPolicyType")] + writable_roots: Annotated[list[AbsolutePathBuf] | None, Field(alias="writableRoots")] = [] + + +class SandboxPolicy( + RootModel[ + DangerFullAccessSandboxPolicy + | ReadOnlySandboxPolicy + | ExternalSandboxSandboxPolicy + | WorkspaceWriteSandboxPolicy + ] +): + model_config = ConfigDict( + populate_by_name=True, + ) + root: ( + DangerFullAccessSandboxPolicy + | ReadOnlySandboxPolicy + | ExternalSandboxSandboxPolicy + | WorkspaceWriteSandboxPolicy + ) + + +class SandboxWorkspaceWrite(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + exclude_slash_tmp: bool | None = False + exclude_tmpdir_env_var: bool | None = False + network_access: bool | None = False + writable_roots: list[str] | None = [] + + +class ItemAgentMessageDeltaServerNotification(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + method: Annotated[ + Literal["item/agentMessage/delta"], + Field(title="Item/agentMessage/deltaNotificationMethod"), + ] + params: AgentMessageDeltaNotification + + +class ItemPlanDeltaServerNotification(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + method: Annotated[Literal["item/plan/delta"], Field(title="Item/plan/deltaNotificationMethod")] + params: PlanDeltaNotification + + +class ItemCommandExecutionOutputDeltaServerNotification(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + method: Annotated[ + Literal["item/commandExecution/outputDelta"], + Field(title="Item/commandExecution/outputDeltaNotificationMethod"), + ] + params: CommandExecutionOutputDeltaNotification + + +class ItemFileChangeOutputDeltaServerNotification(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + method: Annotated[ + Literal["item/fileChange/outputDelta"], + Field(title="Item/fileChange/outputDeltaNotificationMethod"), + ] + params: FileChangeOutputDeltaNotification + + +class ItemMcpToolCallProgressServerNotification(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + method: Annotated[ + Literal["item/mcpToolCall/progress"], + Field(title="Item/mcpToolCall/progressNotificationMethod"), + ] + params: McpToolCallProgressNotification + + +class McpServerOauthLoginCompletedServerNotification(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + method: Annotated[ + Literal["mcpServer/oauthLogin/completed"], + Field(title="McpServer/oauthLogin/completedNotificationMethod"), + ] + params: McpServerOauthLoginCompletedNotification + + +class ItemReasoningSummaryTextDeltaServerNotification(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + method: Annotated[ + Literal["item/reasoning/summaryTextDelta"], + Field(title="Item/reasoning/summaryTextDeltaNotificationMethod"), + ] + params: ReasoningSummaryTextDeltaNotification + + +class ItemReasoningSummaryPartAddedServerNotification(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + method: Annotated[ + Literal["item/reasoning/summaryPartAdded"], + Field(title="Item/reasoning/summaryPartAddedNotificationMethod"), + ] + params: ReasoningSummaryPartAddedNotification + + +class ItemReasoningTextDeltaServerNotification(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + method: Annotated[ + Literal["item/reasoning/textDelta"], + Field(title="Item/reasoning/textDeltaNotificationMethod"), + ] + params: ReasoningTextDeltaNotification + + +class ThreadCompactedServerNotification(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + method: Annotated[ + Literal["thread/compacted"], Field(title="Thread/compactedNotificationMethod") + ] + params: ContextCompactedNotification + + +class ModelReroutedServerNotification(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + method: Annotated[Literal["model/rerouted"], Field(title="Model/reroutedNotificationMethod")] + params: ModelReroutedNotification + + +class DeprecationNoticeServerNotification(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + method: Annotated[ + Literal["deprecationNotice"], Field(title="DeprecationNoticeNotificationMethod") + ] + params: DeprecationNoticeNotification + + +class FuzzyFileSearchSessionUpdatedServerNotification(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + method: Annotated[ + Literal["fuzzyFileSearch/sessionUpdated"], + Field(title="FuzzyFileSearch/sessionUpdatedNotificationMethod"), + ] + params: FuzzyFileSearchSessionUpdatedNotification + + +class FuzzyFileSearchSessionCompletedServerNotification(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + method: Annotated[ + Literal["fuzzyFileSearch/sessionCompleted"], + Field(title="FuzzyFileSearch/sessionCompletedNotificationMethod"), + ] + params: FuzzyFileSearchSessionCompletedNotification + + +class AccountLoginCompletedServerNotification(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + method: Annotated[ + Literal["account/login/completed"], + Field(title="Account/login/completedNotificationMethod"), + ] + params: AccountLoginCompletedNotification + + +class ServerRequestResolvedNotification(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + request_id: Annotated[RequestId, Field(alias="requestId")] + thread_id: Annotated[str, Field(alias="threadId")] + + +class ServiceTier(Enum): + fast = "fast" + flex = "flex" + + +class SessionSourceValue(Enum): + cli = "cli" + vscode = "vscode" + exec = "exec" + app_server = "appServer" + unknown = "unknown" + + +class Settings(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + developer_instructions: str | None = None + model: str + reasoning_effort: ReasoningEffort | None = None + + +class SkillErrorInfo(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + message: str + path: str + + +class SkillInterface(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + brand_color: Annotated[str | None, Field(alias="brandColor")] = None + default_prompt: Annotated[str | None, Field(alias="defaultPrompt")] = None + display_name: Annotated[str | None, Field(alias="displayName")] = None + icon_large: Annotated[str | None, Field(alias="iconLarge")] = None + icon_small: Annotated[str | None, Field(alias="iconSmall")] = None + short_description: Annotated[str | None, Field(alias="shortDescription")] = None + + +class SkillScope(Enum): + user = "user" + repo = "repo" + system = "system" + admin = "admin" + + +class SkillSummary(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + description: str + interface: SkillInterface | None = None + name: str + path: str + short_description: Annotated[str | None, Field(alias="shortDescription")] = None + + +class SkillToolDependency(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + command: str | None = None + description: str | None = None + transport: str | None = None + type: str + url: str | None = None + value: str + + +class SkillsChangedNotification(BaseModel): + pass + model_config = ConfigDict( + populate_by_name=True, + ) + + +class SkillsConfigWriteParams(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + enabled: bool + path: str + + +class SkillsConfigWriteResponse(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + effective_enabled: Annotated[bool, Field(alias="effectiveEnabled")] + + +class SkillsListExtraRootsForCwd(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + cwd: str + extra_user_roots: Annotated[list[str], Field(alias="extraUserRoots")] + + +class SkillsListParams(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + cwds: Annotated[ + list[str] | None, + Field(description="When empty, defaults to the current session working directory."), + ] = None + force_reload: Annotated[ + bool | None, + Field( + alias="forceReload", + description="When true, bypass the skills cache and re-scan skills from disk.", + ), + ] = None + per_cwd_extra_user_roots: Annotated[ + list[SkillsListExtraRootsForCwd] | None, + Field( + alias="perCwdExtraUserRoots", + description="Optional per-cwd extra roots to scan as user-scoped skills.", + ), + ] = None + + +class SkillsRemoteReadParams(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + enabled: bool | None = False + hazelnut_scope: Annotated[HazelnutScope | None, Field(alias="hazelnutScope")] = "example" + product_surface: Annotated[ProductSurface | None, Field(alias="productSurface")] = "codex" + + +class SkillsRemoteReadResponse(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + data: list[RemoteSkillSummary] + + +class SkillsRemoteWriteParams(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + hazelnut_id: Annotated[str, Field(alias="hazelnutId")] + + +class SkillsRemoteWriteResponse(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + id: str + path: str + + +class SubAgentSourceValue(Enum): + review = "review" + compact = "compact" + memory_consolidation = "memory_consolidation" + + +class OtherSubAgentSource(BaseModel): + model_config = ConfigDict( + extra="forbid", + populate_by_name=True, + ) + other: str + + +class TerminalInteractionNotification(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + item_id: Annotated[str, Field(alias="itemId")] + process_id: Annotated[str, Field(alias="processId")] + stdin: str + thread_id: Annotated[str, Field(alias="threadId")] + turn_id: Annotated[str, Field(alias="turnId")] + + +class TextElement(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + byte_range: Annotated[ + ByteRange, + Field( + alias="byteRange", + description="Byte range in the parent `text` buffer that this element occupies.", + ), + ] + placeholder: Annotated[ + str | None, + Field( + description="Optional human-readable placeholder for the element, displayed in the UI." + ), + ] = None + + +class TextPosition(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + column: Annotated[ + int, + Field(description="1-based column number (in Unicode scalar values).", ge=0), + ] + line: Annotated[int, Field(description="1-based line number.", ge=0)] + + +class TextRange(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + end: TextPosition + start: TextPosition + + +class ThreadActiveFlag(Enum): + waiting_on_approval = "waitingOnApproval" + waiting_on_user_input = "waitingOnUserInput" + + +class ThreadArchiveParams(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + thread_id: Annotated[str, Field(alias="threadId")] + + +class ThreadArchiveResponse(BaseModel): + pass + model_config = ConfigDict( + populate_by_name=True, + ) + + +class ThreadArchivedNotification(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + thread_id: Annotated[str, Field(alias="threadId")] + + +class ThreadClosedNotification(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + thread_id: Annotated[str, Field(alias="threadId")] + + +class ThreadCompactStartParams(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + thread_id: Annotated[str, Field(alias="threadId")] + + +class ThreadCompactStartResponse(BaseModel): + pass + model_config = ConfigDict( + populate_by_name=True, + ) + + +class ThreadForkParams(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + approval_policy: Annotated[AskForApproval | None, Field(alias="approvalPolicy")] = None + approvals_reviewer: Annotated[ + ApprovalsReviewer | None, + Field( + alias="approvalsReviewer", + description="Override where approval requests are routed for review on this thread and subsequent turns.", + ), + ] = None + base_instructions: Annotated[str | None, Field(alias="baseInstructions")] = None + config: dict[str, Any] | None = None + cwd: str | None = None + developer_instructions: Annotated[str | None, Field(alias="developerInstructions")] = None + ephemeral: bool | None = None + model: Annotated[ + str | None, + Field(description="Configuration overrides for the forked thread, if any."), + ] = None + model_provider: Annotated[str | None, Field(alias="modelProvider")] = None + sandbox: SandboxMode | None = None + service_tier: Annotated[ServiceTier | None, Field(alias="serviceTier")] = None + thread_id: Annotated[str, Field(alias="threadId")] + + +class ThreadId(RootModel[str]): + model_config = ConfigDict( + populate_by_name=True, + ) + root: str + + +class AgentMessageThreadItem(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + id: str + phase: MessagePhase | None = None + text: str + type: Annotated[Literal["agentMessage"], Field(title="AgentMessageThreadItemType")] + + +class PlanThreadItem(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + id: str + text: str + type: Annotated[Literal["plan"], Field(title="PlanThreadItemType")] + + +class ReasoningThreadItem(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + content: list[str] | None = [] + id: str + summary: list[str] | None = [] + type: Annotated[Literal["reasoning"], Field(title="ReasoningThreadItemType")] + + +class CommandExecutionThreadItem(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + aggregated_output: Annotated[ + str | None, + Field( + alias="aggregatedOutput", + description="The command's output, aggregated from stdout and stderr.", + ), + ] = None + command: Annotated[str, Field(description="The command to be executed.")] + command_actions: Annotated[ + list[CommandAction], + Field( + alias="commandActions", + description="A best-effort parsing of the command to understand the action(s) it will perform. This returns a list of CommandAction objects because a single shell command may be composed of many commands piped together.", + ), + ] + cwd: Annotated[str, Field(description="The command's working directory.")] + duration_ms: Annotated[ + int | None, + Field( + alias="durationMs", + description="The duration of the command execution in milliseconds.", + ), + ] = None + exit_code: Annotated[ + int | None, Field(alias="exitCode", description="The command's exit code.") + ] = None + id: str + process_id: Annotated[ + str | None, + Field( + alias="processId", + description="Identifier for the underlying PTY process (when available).", + ), + ] = None + status: CommandExecutionStatus + type: Annotated[Literal["commandExecution"], Field(title="CommandExecutionThreadItemType")] + + +class McpToolCallThreadItem(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + arguments: Any + duration_ms: Annotated[ + int | None, + Field( + alias="durationMs", + description="The duration of the MCP tool call in milliseconds.", + ), + ] = None + error: McpToolCallError | None = None + id: str + result: McpToolCallResult | None = None + server: str + status: McpToolCallStatus + tool: str + type: Annotated[Literal["mcpToolCall"], Field(title="McpToolCallThreadItemType")] + + +class DynamicToolCallThreadItem(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + arguments: Any + content_items: Annotated[ + list[DynamicToolCallOutputContentItem] | None, Field(alias="contentItems") + ] = None + duration_ms: Annotated[ + int | None, + Field( + alias="durationMs", + description="The duration of the dynamic tool call in milliseconds.", + ), + ] = None + id: str + status: DynamicToolCallStatus + success: bool | None = None + tool: str + type: Annotated[Literal["dynamicToolCall"], Field(title="DynamicToolCallThreadItemType")] + + +class ImageViewThreadItem(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + id: str + path: str + type: Annotated[Literal["imageView"], Field(title="ImageViewThreadItemType")] + + +class ImageGenerationThreadItem(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + id: str + result: str + revised_prompt: Annotated[str | None, Field(alias="revisedPrompt")] = None + status: str + type: Annotated[Literal["imageGeneration"], Field(title="ImageGenerationThreadItemType")] + + +class EnteredReviewModeThreadItem(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + id: str + review: str + type: Annotated[Literal["enteredReviewMode"], Field(title="EnteredReviewModeThreadItemType")] + + +class ExitedReviewModeThreadItem(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + id: str + review: str + type: Annotated[Literal["exitedReviewMode"], Field(title="ExitedReviewModeThreadItemType")] + + +class ContextCompactionThreadItem(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + id: str + type: Annotated[Literal["contextCompaction"], Field(title="ContextCompactionThreadItemType")] + + +class ThreadLoadedListParams(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + cursor: Annotated[ + str | None, + Field(description="Opaque pagination cursor returned by a previous call."), + ] = None + limit: Annotated[ + int | None, Field(description="Optional page size; defaults to no limit.", ge=0) + ] = None + + +class ThreadLoadedListResponse(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + data: Annotated[ + list[str], + Field(description="Thread ids for sessions currently loaded in memory."), + ] + next_cursor: Annotated[ + str | None, + Field( + alias="nextCursor", + description="Opaque cursor to pass to the next call to continue after the last item. if None, there are no more items to return.", + ), + ] = None + + +class ThreadMetadataGitInfoUpdateParams(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + branch: Annotated[ + str | None, + Field( + description="Omit to leave the stored branch unchanged, set to `null` to clear it, or provide a non-empty string to replace it." + ), + ] = None + origin_url: Annotated[ + str | None, + Field( + alias="originUrl", + description="Omit to leave the stored origin URL unchanged, set to `null` to clear it, or provide a non-empty string to replace it.", + ), + ] = None + sha: Annotated[ + str | None, + Field( + description="Omit to leave the stored commit unchanged, set to `null` to clear it, or provide a non-empty string to replace it." + ), + ] = None + + +class ThreadMetadataUpdateParams(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + git_info: Annotated[ + ThreadMetadataGitInfoUpdateParams | None, + Field( + alias="gitInfo", + description="Patch the stored Git metadata for this thread. Omit a field to leave it unchanged, set it to `null` to clear it, or provide a string to replace the stored value.", + ), + ] = None + thread_id: Annotated[str, Field(alias="threadId")] + + +class ThreadNameUpdatedNotification(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + thread_id: Annotated[str, Field(alias="threadId")] + thread_name: Annotated[str | None, Field(alias="threadName")] = None + + +class ThreadReadParams(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + include_turns: Annotated[ + bool | None, + Field( + alias="includeTurns", + description="When true, include turns and their items from rollout history.", + ), + ] = False + thread_id: Annotated[str, Field(alias="threadId")] + + +class ThreadRealtimeAudioChunk(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + data: str + num_channels: Annotated[int, Field(alias="numChannels", ge=0)] + sample_rate: Annotated[int, Field(alias="sampleRate", ge=0)] + samples_per_channel: Annotated[int | None, Field(alias="samplesPerChannel", ge=0)] = None + + +class ThreadRealtimeClosedNotification(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + reason: str | None = None + thread_id: Annotated[str, Field(alias="threadId")] + + +class ThreadRealtimeErrorNotification(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + message: str + thread_id: Annotated[str, Field(alias="threadId")] + + +class ThreadRealtimeItemAddedNotification(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + item: Any + thread_id: Annotated[str, Field(alias="threadId")] + + +class ThreadRealtimeOutputAudioDeltaNotification(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + audio: ThreadRealtimeAudioChunk + thread_id: Annotated[str, Field(alias="threadId")] + + +class ThreadRealtimeStartedNotification(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + session_id: Annotated[str | None, Field(alias="sessionId")] = None + thread_id: Annotated[str, Field(alias="threadId")] + + +class ThreadResumeParams(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + approval_policy: Annotated[AskForApproval | None, Field(alias="approvalPolicy")] = None + approvals_reviewer: Annotated[ + ApprovalsReviewer | None, + Field( + alias="approvalsReviewer", + description="Override where approval requests are routed for review on this thread and subsequent turns.", + ), + ] = None + base_instructions: Annotated[str | None, Field(alias="baseInstructions")] = None + config: dict[str, Any] | None = None + cwd: str | None = None + developer_instructions: Annotated[str | None, Field(alias="developerInstructions")] = None + model: Annotated[ + str | None, + Field(description="Configuration overrides for the resumed thread, if any."), + ] = None + model_provider: Annotated[str | None, Field(alias="modelProvider")] = None + personality: Personality | None = None + sandbox: SandboxMode | None = None + service_tier: Annotated[ServiceTier | None, Field(alias="serviceTier")] = None + thread_id: Annotated[str, Field(alias="threadId")] + + +class ThreadRollbackParams(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + num_turns: Annotated[ + int, + Field( + alias="numTurns", + description="The number of turns to drop from the end of the thread. Must be >= 1.\n\nThis only modifies the thread's history and does not revert local file changes that have been made by the agent. Clients are responsible for reverting these changes.", + ge=0, + ), + ] + thread_id: Annotated[str, Field(alias="threadId")] + + +class ThreadSetNameParams(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + name: str + thread_id: Annotated[str, Field(alias="threadId")] + + +class ThreadSetNameResponse(BaseModel): + pass + model_config = ConfigDict( + populate_by_name=True, + ) + + +class ThreadSortKey(Enum): + created_at = "created_at" + updated_at = "updated_at" + + +class ThreadSourceKind(Enum): + cli = "cli" + vscode = "vscode" + exec = "exec" + app_server = "appServer" + sub_agent = "subAgent" + sub_agent_review = "subAgentReview" + sub_agent_compact = "subAgentCompact" + sub_agent_thread_spawn = "subAgentThreadSpawn" + sub_agent_other = "subAgentOther" + unknown = "unknown" + + +class ThreadStartParams(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + approval_policy: Annotated[AskForApproval | None, Field(alias="approvalPolicy")] = None + approvals_reviewer: Annotated[ + ApprovalsReviewer | None, + Field( + alias="approvalsReviewer", + description="Override where approval requests are routed for review on this thread and subsequent turns.", + ), + ] = None + base_instructions: Annotated[str | None, Field(alias="baseInstructions")] = None + config: dict[str, Any] | None = None + cwd: str | None = None + developer_instructions: Annotated[str | None, Field(alias="developerInstructions")] = None + ephemeral: bool | None = None + model: str | None = None + model_provider: Annotated[str | None, Field(alias="modelProvider")] = None + personality: Personality | None = None + sandbox: SandboxMode | None = None + service_name: Annotated[str | None, Field(alias="serviceName")] = None + service_tier: Annotated[ServiceTier | None, Field(alias="serviceTier")] = None + + +class NotLoadedThreadStatus(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + type: Annotated[Literal["notLoaded"], Field(title="NotLoadedThreadStatusType")] + + +class IdleThreadStatus(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + type: Annotated[Literal["idle"], Field(title="IdleThreadStatusType")] + + +class SystemErrorThreadStatus(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + type: Annotated[Literal["systemError"], Field(title="SystemErrorThreadStatusType")] + + +class ActiveThreadStatus(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + active_flags: Annotated[list[ThreadActiveFlag], Field(alias="activeFlags")] + type: Annotated[Literal["active"], Field(title="ActiveThreadStatusType")] + + +class ThreadStatus( + RootModel[ + NotLoadedThreadStatus | IdleThreadStatus | SystemErrorThreadStatus | ActiveThreadStatus + ] +): + model_config = ConfigDict( + populate_by_name=True, + ) + root: NotLoadedThreadStatus | IdleThreadStatus | SystemErrorThreadStatus | ActiveThreadStatus + + +class ThreadStatusChangedNotification(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + status: ThreadStatus + thread_id: Annotated[str, Field(alias="threadId")] + + +class ThreadUnarchiveParams(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + thread_id: Annotated[str, Field(alias="threadId")] + + +class ThreadUnarchivedNotification(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + thread_id: Annotated[str, Field(alias="threadId")] + + +class ThreadUnsubscribeParams(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + thread_id: Annotated[str, Field(alias="threadId")] + + +class ThreadUnsubscribeStatus(Enum): + not_loaded = "notLoaded" + not_subscribed = "notSubscribed" + unsubscribed = "unsubscribed" + + +class TokenUsageBreakdown(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + cached_input_tokens: Annotated[int, Field(alias="cachedInputTokens")] + input_tokens: Annotated[int, Field(alias="inputTokens")] + output_tokens: Annotated[int, Field(alias="outputTokens")] + reasoning_output_tokens: Annotated[int, Field(alias="reasoningOutputTokens")] + total_tokens: Annotated[int, Field(alias="totalTokens")] + + +class Tool(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + field_meta: Annotated[Any | None, Field(alias="_meta")] = None + annotations: Any | None = None + description: str | None = None + icons: list | None = None + input_schema: Annotated[Any, Field(alias="inputSchema")] + name: str + output_schema: Annotated[Any | None, Field(alias="outputSchema")] = None + title: str | None = None + + +class TurnDiffUpdatedNotification(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + diff: str + thread_id: Annotated[str, Field(alias="threadId")] + turn_id: Annotated[str, Field(alias="turnId")] + + +class TurnError(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + additional_details: Annotated[str | None, Field(alias="additionalDetails")] = None + codex_error_info: Annotated[CodexErrorInfo | None, Field(alias="codexErrorInfo")] = None + message: str + + +class TurnInterruptParams(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + thread_id: Annotated[str, Field(alias="threadId")] + turn_id: Annotated[str, Field(alias="turnId")] + + +class TurnInterruptResponse(BaseModel): + pass + model_config = ConfigDict( + populate_by_name=True, + ) + + +class TurnPlanStepStatus(Enum): + pending = "pending" + in_progress = "inProgress" + completed = "completed" + + +class TurnStatus(Enum): + completed = "completed" + interrupted = "interrupted" + failed = "failed" + in_progress = "inProgress" + + +class TurnSteerResponse(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + turn_id: Annotated[str, Field(alias="turnId")] + + +class TextUserInput(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + text: str + text_elements: Annotated[ + list[TextElement] | None, + Field( + description="UI-defined spans within `text` used to render or persist special elements." + ), + ] = [] + type: Annotated[Literal["text"], Field(title="TextUserInputType")] + + +class ImageUserInput(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + type: Annotated[Literal["image"], Field(title="ImageUserInputType")] + url: str + + +class LocalImageUserInput(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + path: str + type: Annotated[Literal["localImage"], Field(title="LocalImageUserInputType")] + + +class SkillUserInput(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + name: str + path: str + type: Annotated[Literal["skill"], Field(title="SkillUserInputType")] + + +class MentionUserInput(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + name: str + path: str + type: Annotated[Literal["mention"], Field(title="MentionUserInputType")] + + +class UserInput( + RootModel[ + TextUserInput | ImageUserInput | LocalImageUserInput | SkillUserInput | MentionUserInput + ] +): + model_config = ConfigDict( + populate_by_name=True, + ) + root: TextUserInput | ImageUserInput | LocalImageUserInput | SkillUserInput | MentionUserInput + + +class Verbosity(Enum): + low = "low" + medium = "medium" + high = "high" + + +class SearchWebSearchAction(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + queries: list[str] | None = None + query: str | None = None + type: Annotated[Literal["search"], Field(title="SearchWebSearchActionType")] + + +class OpenPageWebSearchAction(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + type: Annotated[Literal["openPage"], Field(title="OpenPageWebSearchActionType")] + url: str | None = None + + +class FindInPageWebSearchAction(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + pattern: str | None = None + type: Annotated[Literal["findInPage"], Field(title="FindInPageWebSearchActionType")] + url: str | None = None + + +class OtherWebSearchAction(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + type: Annotated[Literal["other"], Field(title="OtherWebSearchActionType")] + + +class WebSearchAction( + RootModel[ + SearchWebSearchAction + | OpenPageWebSearchAction + | FindInPageWebSearchAction + | OtherWebSearchAction + ] +): + model_config = ConfigDict( + populate_by_name=True, + ) + root: ( + SearchWebSearchAction + | OpenPageWebSearchAction + | FindInPageWebSearchAction + | OtherWebSearchAction + ) + + +class WebSearchContextSize(Enum): + low = "low" + medium = "medium" + high = "high" + + +class WebSearchLocation(BaseModel): + model_config = ConfigDict( + extra="forbid", + populate_by_name=True, + ) + city: str | None = None + country: str | None = None + region: str | None = None + timezone: str | None = None + + +class WebSearchMode(Enum): + disabled = "disabled" + cached = "cached" + live = "live" + + +class WebSearchToolConfig(BaseModel): + model_config = ConfigDict( + extra="forbid", + populate_by_name=True, + ) + allowed_domains: list[str] | None = None + context_size: WebSearchContextSize | None = None + location: WebSearchLocation | None = None + + +class WindowsSandboxSetupMode(Enum): + elevated = "elevated" + unelevated = "unelevated" + + +class WindowsSandboxSetupStartParams(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + cwd: AbsolutePathBuf | None = None + mode: WindowsSandboxSetupMode + + +class WindowsSandboxSetupStartResponse(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + started: bool + + +class WindowsWorldWritableWarningNotification(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + extra_count: Annotated[int, Field(alias="extraCount", ge=0)] + failed_scan: Annotated[bool, Field(alias="failedScan")] + sample_paths: Annotated[list[str], Field(alias="samplePaths")] + + +class WriteStatus(Enum): + ok = "ok" + ok_overridden = "okOverridden" + + +class ChatgptAccount(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + email: str + plan_type: Annotated[PlanType, Field(alias="planType")] + type: Annotated[Literal["chatgpt"], Field(title="ChatgptAccountType")] + + +class Account(RootModel[ApiKeyAccount | ChatgptAccount]): + model_config = ConfigDict( + populate_by_name=True, + ) + root: ApiKeyAccount | ChatgptAccount + + +class AccountUpdatedNotification(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + auth_mode: Annotated[AuthMode | None, Field(alias="authMode")] = None + plan_type: Annotated[PlanType | None, Field(alias="planType")] = None + + +class AppConfig(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + default_tools_approval_mode: AppToolApproval | None = None + default_tools_enabled: bool | None = None + destructive_enabled: bool | None = None + enabled: bool | None = True + open_world_enabled: bool | None = None + tools: AppToolsConfig | None = None + + +class AppMetadata(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + categories: list[str] | None = None + developer: str | None = None + first_party_requires_install: Annotated[ + bool | None, Field(alias="firstPartyRequiresInstall") + ] = None + first_party_type: Annotated[str | None, Field(alias="firstPartyType")] = None + review: AppReview | None = None + screenshots: list[AppScreenshot] | None = None + seo_description: Annotated[str | None, Field(alias="seoDescription")] = None + show_in_composer_when_unlinked: Annotated[ + bool | None, Field(alias="showInComposerWhenUnlinked") + ] = None + sub_categories: Annotated[list[str] | None, Field(alias="subCategories")] = None + version: str | None = None + version_id: Annotated[str | None, Field(alias="versionId")] = None + version_notes: Annotated[str | None, Field(alias="versionNotes")] = None + + +class AppsConfig(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + field_default: Annotated[AppsDefaultConfig | None, Field(alias="_default")] = None + + +class CancelLoginAccountResponse(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + status: CancelLoginAccountStatus + + +class InitializeRequest(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + id: RequestId + method: Annotated[Literal["initialize"], Field(title="InitializeRequestMethod")] + params: InitializeParams + + +class ThreadStartRequest(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + id: RequestId + method: Annotated[Literal["thread/start"], Field(title="Thread/startRequestMethod")] + params: ThreadStartParams + + +class ThreadResumeRequest(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + id: RequestId + method: Annotated[Literal["thread/resume"], Field(title="Thread/resumeRequestMethod")] + params: ThreadResumeParams + + +class ThreadForkRequest(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + id: RequestId + method: Annotated[Literal["thread/fork"], Field(title="Thread/forkRequestMethod")] + params: ThreadForkParams + + +class ThreadArchiveRequest(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + id: RequestId + method: Annotated[Literal["thread/archive"], Field(title="Thread/archiveRequestMethod")] + params: ThreadArchiveParams + + +class ThreadUnsubscribeRequest(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + id: RequestId + method: Annotated[Literal["thread/unsubscribe"], Field(title="Thread/unsubscribeRequestMethod")] + params: ThreadUnsubscribeParams + + +class ThreadNameSetRequest(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + id: RequestId + method: Annotated[Literal["thread/name/set"], Field(title="Thread/name/setRequestMethod")] + params: ThreadSetNameParams + + +class ThreadMetadataUpdateRequest(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + id: RequestId + method: Annotated[ + Literal["thread/metadata/update"], + Field(title="Thread/metadata/updateRequestMethod"), + ] + params: ThreadMetadataUpdateParams + + +class ThreadUnarchiveRequest(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + id: RequestId + method: Annotated[Literal["thread/unarchive"], Field(title="Thread/unarchiveRequestMethod")] + params: ThreadUnarchiveParams + + +class ThreadCompactStartRequest(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + id: RequestId + method: Annotated[ + Literal["thread/compact/start"], + Field(title="Thread/compact/startRequestMethod"), + ] + params: ThreadCompactStartParams + + +class ThreadRollbackRequest(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + id: RequestId + method: Annotated[Literal["thread/rollback"], Field(title="Thread/rollbackRequestMethod")] + params: ThreadRollbackParams + + +class ThreadLoadedListRequest(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + id: RequestId + method: Annotated[Literal["thread/loaded/list"], Field(title="Thread/loaded/listRequestMethod")] + params: ThreadLoadedListParams + + +class ThreadReadRequest(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + id: RequestId + method: Annotated[Literal["thread/read"], Field(title="Thread/readRequestMethod")] + params: ThreadReadParams + + +class SkillsListRequest(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + id: RequestId + method: Annotated[Literal["skills/list"], Field(title="Skills/listRequestMethod")] + params: SkillsListParams + + +class PluginListRequest(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + id: RequestId + method: Annotated[Literal["plugin/list"], Field(title="Plugin/listRequestMethod")] + params: PluginListParams + + +class PluginReadRequest(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + id: RequestId + method: Annotated[Literal["plugin/read"], Field(title="Plugin/readRequestMethod")] + params: PluginReadParams + + +class SkillsRemoteListRequest(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + id: RequestId + method: Annotated[Literal["skills/remote/list"], Field(title="Skills/remote/listRequestMethod")] + params: SkillsRemoteReadParams + + +class SkillsRemoteExportRequest(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + id: RequestId + method: Annotated[ + Literal["skills/remote/export"], + Field(title="Skills/remote/exportRequestMethod"), + ] + params: SkillsRemoteWriteParams + + +class AppListRequest(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + id: RequestId + method: Annotated[Literal["app/list"], Field(title="App/listRequestMethod")] + params: AppsListParams + + +class FsReadFileRequest(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + id: RequestId + method: Annotated[Literal["fs/readFile"], Field(title="Fs/readFileRequestMethod")] + params: FsReadFileParams + + +class FsWriteFileRequest(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + id: RequestId + method: Annotated[Literal["fs/writeFile"], Field(title="Fs/writeFileRequestMethod")] + params: FsWriteFileParams + + +class FsCreateDirectoryRequest(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + id: RequestId + method: Annotated[Literal["fs/createDirectory"], Field(title="Fs/createDirectoryRequestMethod")] + params: FsCreateDirectoryParams + + +class FsGetMetadataRequest(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + id: RequestId + method: Annotated[Literal["fs/getMetadata"], Field(title="Fs/getMetadataRequestMethod")] + params: FsGetMetadataParams + + +class FsReadDirectoryRequest(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + id: RequestId + method: Annotated[Literal["fs/readDirectory"], Field(title="Fs/readDirectoryRequestMethod")] + params: FsReadDirectoryParams + + +class FsRemoveRequest(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + id: RequestId + method: Annotated[Literal["fs/remove"], Field(title="Fs/removeRequestMethod")] + params: FsRemoveParams + + +class FsCopyRequest(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + id: RequestId + method: Annotated[Literal["fs/copy"], Field(title="Fs/copyRequestMethod")] + params: FsCopyParams + + +class SkillsConfigWriteRequest(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + id: RequestId + method: Annotated[ + Literal["skills/config/write"], Field(title="Skills/config/writeRequestMethod") + ] + params: SkillsConfigWriteParams + + +class PluginInstallRequest(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + id: RequestId + method: Annotated[Literal["plugin/install"], Field(title="Plugin/installRequestMethod")] + params: PluginInstallParams + + +class PluginUninstallRequest(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + id: RequestId + method: Annotated[Literal["plugin/uninstall"], Field(title="Plugin/uninstallRequestMethod")] + params: PluginUninstallParams + + +class TurnInterruptRequest(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + id: RequestId + method: Annotated[Literal["turn/interrupt"], Field(title="Turn/interruptRequestMethod")] + params: TurnInterruptParams + + +class ModelListRequest(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + id: RequestId + method: Annotated[Literal["model/list"], Field(title="Model/listRequestMethod")] + params: ModelListParams + + +class ExperimentalFeatureListRequest(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + id: RequestId + method: Annotated[ + Literal["experimentalFeature/list"], + Field(title="ExperimentalFeature/listRequestMethod"), + ] + params: ExperimentalFeatureListParams + + +class McpServerOauthLoginRequest(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + id: RequestId + method: Annotated[ + Literal["mcpServer/oauth/login"], + Field(title="McpServer/oauth/loginRequestMethod"), + ] + params: McpServerOauthLoginParams + + +class ConfigMcpServerReloadRequest(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + id: RequestId + method: Annotated[ + Literal["config/mcpServer/reload"], + Field(title="Config/mcpServer/reloadRequestMethod"), + ] + params: None = None + + +class McpServerStatusListRequest(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + id: RequestId + method: Annotated[ + Literal["mcpServerStatus/list"], + Field(title="McpServerStatus/listRequestMethod"), + ] + params: ListMcpServerStatusParams + + +class WindowsSandboxSetupStartRequest(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + id: RequestId + method: Annotated[ + Literal["windowsSandbox/setupStart"], + Field(title="WindowsSandbox/setupStartRequestMethod"), + ] + params: WindowsSandboxSetupStartParams + + +class AccountLoginStartRequest(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + id: RequestId + method: Annotated[ + Literal["account/login/start"], Field(title="Account/login/startRequestMethod") + ] + params: LoginAccountParams + + +class AccountLoginCancelRequest(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + id: RequestId + method: Annotated[ + Literal["account/login/cancel"], + Field(title="Account/login/cancelRequestMethod"), + ] + params: CancelLoginAccountParams + + +class AccountLogoutRequest(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + id: RequestId + method: Annotated[Literal["account/logout"], Field(title="Account/logoutRequestMethod")] + params: None = None + + +class AccountRateLimitsReadRequest(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + id: RequestId + method: Annotated[ + Literal["account/rateLimits/read"], + Field(title="Account/rateLimits/readRequestMethod"), + ] + params: None = None + + +class FeedbackUploadRequest(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + id: RequestId + method: Annotated[Literal["feedback/upload"], Field(title="Feedback/uploadRequestMethod")] + params: FeedbackUploadParams + + +class CommandExecWriteRequest(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + id: RequestId + method: Annotated[Literal["command/exec/write"], Field(title="Command/exec/writeRequestMethod")] + params: CommandExecWriteParams + + +class CommandExecTerminateRequest(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + id: RequestId + method: Annotated[ + Literal["command/exec/terminate"], + Field(title="Command/exec/terminateRequestMethod"), + ] + params: CommandExecTerminateParams + + +class ConfigReadRequest(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + id: RequestId + method: Annotated[Literal["config/read"], Field(title="Config/readRequestMethod")] + params: ConfigReadParams + + +class ExternalAgentConfigDetectRequest(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + id: RequestId + method: Annotated[ + Literal["externalAgentConfig/detect"], + Field(title="ExternalAgentConfig/detectRequestMethod"), + ] + params: ExternalAgentConfigDetectParams + + +class ConfigRequirementsReadRequest(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + id: RequestId + method: Annotated[ + Literal["configRequirements/read"], + Field(title="ConfigRequirements/readRequestMethod"), + ] + params: None = None + + +class AccountReadRequest(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + id: RequestId + method: Annotated[Literal["account/read"], Field(title="Account/readRequestMethod")] + params: GetAccountParams + + +class FuzzyFileSearchRequest(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + id: RequestId + method: Annotated[Literal["fuzzyFileSearch"], Field(title="FuzzyFileSearchRequestMethod")] + params: FuzzyFileSearchParams + + +class CollabAgentState(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + message: str | None = None + status: CollabAgentStatus + + +class CollaborationMode(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + mode: ModeKind + settings: Settings + + +class CollaborationModeMask(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + mode: ModeKind | None = None + model: str | None = None + name: str + reasoning_effort: ReasoningEffort | None = None + + +class CommandExecOutputDeltaNotification(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + cap_reached: Annotated[ + bool, + Field( + alias="capReached", + description="`true` on the final streamed chunk for a stream when `outputBytesCap` truncated later output on that stream.", + ), + ] + delta_base64: Annotated[ + str, Field(alias="deltaBase64", description="Base64-encoded output bytes.") + ] + process_id: Annotated[ + str, + Field( + alias="processId", + description="Client-supplied, connection-scoped `processId` from the original `command/exec` request.", + ), + ] + stream: Annotated[CommandExecOutputStream, Field(description="Output stream for this chunk.")] + + +class CommandExecParams(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + command: Annotated[ + list[str], Field(description="Command argv vector. Empty arrays are rejected.") + ] + cwd: Annotated[ + str | None, + Field(description="Optional working directory. Defaults to the server cwd."), + ] = None + disable_output_cap: Annotated[ + bool | None, + Field( + alias="disableOutputCap", + description="Disable stdout/stderr capture truncation for this request.\n\nCannot be combined with `outputBytesCap`.", + ), + ] = None + disable_timeout: Annotated[ + bool | None, + Field( + alias="disableTimeout", + description="Disable the timeout entirely for this request.\n\nCannot be combined with `timeoutMs`.", + ), + ] = None + env: Annotated[ + dict[str, Any] | None, + Field( + description="Optional environment overrides merged into the server-computed environment.\n\nMatching names override inherited values. Set a key to `null` to unset an inherited variable." + ), + ] = None + output_bytes_cap: Annotated[ + int | None, + Field( + alias="outputBytesCap", + description="Optional per-stream stdout/stderr capture cap in bytes.\n\nWhen omitted, the server default applies. Cannot be combined with `disableOutputCap`.", + ge=0, + ), + ] = None + process_id: Annotated[ + str | None, + Field( + alias="processId", + description="Optional client-supplied, connection-scoped process id.\n\nRequired for `tty`, `streamStdin`, `streamStdoutStderr`, and follow-up `command/exec/write`, `command/exec/resize`, and `command/exec/terminate` calls. When omitted, buffered execution gets an internal id that is not exposed to the client.", + ), + ] = None + sandbox_policy: Annotated[ + SandboxPolicy | None, + Field( + alias="sandboxPolicy", + description="Optional sandbox policy for this command.\n\nUses the same shape as thread/turn execution sandbox configuration and defaults to the user's configured policy when omitted.", + ), + ] = None + size: Annotated[ + CommandExecTerminalSize | None, + Field( + description="Optional initial PTY size in character cells. Only valid when `tty` is true." + ), + ] = None + stream_stdin: Annotated[ + bool | None, + Field( + alias="streamStdin", + description="Allow follow-up `command/exec/write` requests to write stdin bytes.\n\nRequires a client-supplied `processId`.", + ), + ] = None + stream_stdout_stderr: Annotated[ + bool | None, + Field( + alias="streamStdoutStderr", + description="Stream stdout/stderr via `command/exec/outputDelta` notifications.\n\nStreamed bytes are not duplicated into the final response and require a client-supplied `processId`.", + ), + ] = None + timeout_ms: Annotated[ + int | None, + Field( + alias="timeoutMs", + description="Optional timeout in milliseconds.\n\nWhen omitted, the server default applies. Cannot be combined with `disableTimeout`.", + ), + ] = None + tty: Annotated[ + bool | None, + Field( + description="Enable PTY mode.\n\nThis implies `streamStdin` and `streamStdoutStderr`." + ), + ] = None + + +class CommandExecResizeParams(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + process_id: Annotated[ + str, + Field( + alias="processId", + description="Client-supplied, connection-scoped `processId` from the original `command/exec` request.", + ), + ] + size: Annotated[CommandExecTerminalSize, Field(description="New PTY size in character cells.")] + + +class ConfigEdit(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + key_path: Annotated[str, Field(alias="keyPath")] + merge_strategy: Annotated[MergeStrategy, Field(alias="mergeStrategy")] + value: Any + + +class ConfigLayer(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + config: Any + disabled_reason: Annotated[str | None, Field(alias="disabledReason")] = None + name: ConfigLayerSource + version: str + + +class ConfigLayerMetadata(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + name: ConfigLayerSource + version: str + + +class ConfigRequirements(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + allowed_approval_policies: Annotated[ + list[AskForApproval] | None, Field(alias="allowedApprovalPolicies") + ] = None + allowed_sandbox_modes: Annotated[ + list[SandboxMode] | None, Field(alias="allowedSandboxModes") + ] = None + allowed_web_search_modes: Annotated[ + list[WebSearchMode] | None, Field(alias="allowedWebSearchModes") + ] = None + enforce_residency: Annotated[ResidencyRequirement | None, Field(alias="enforceResidency")] = ( + None + ) + feature_requirements: Annotated[dict[str, Any] | None, Field(alias="featureRequirements")] = ( + None + ) + + +class ConfigRequirementsReadResponse(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + requirements: Annotated[ + ConfigRequirements | None, + Field( + description="Null if no requirements are configured (e.g. no requirements.toml/MDM entries)." + ), + ] = None + + +class ConfigValueWriteParams(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + expected_version: Annotated[str | None, Field(alias="expectedVersion")] = None + file_path: Annotated[ + str | None, + Field( + alias="filePath", + description="Path to the config file to write; defaults to the user's `config.toml` when omitted.", + ), + ] = None + key_path: Annotated[str, Field(alias="keyPath")] + merge_strategy: Annotated[MergeStrategy, Field(alias="mergeStrategy")] + value: Any + + +class ConfigWarningNotification(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + details: Annotated[ + str | None, Field(description="Optional extra guidance or error details.") + ] = None + path: Annotated[ + str | None, + Field(description="Optional path to the config file that triggered the warning."), + ] = None + range: Annotated[ + TextRange | None, + Field(description="Optional range for the error location inside the config file."), + ] = None + summary: Annotated[str, Field(description="Concise summary of the warning.")] + + +class ErrorNotification(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + error: TurnError + thread_id: Annotated[str, Field(alias="threadId")] + turn_id: Annotated[str, Field(alias="turnId")] + will_retry: Annotated[bool, Field(alias="willRetry")] + + +class ExperimentalFeature(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + announcement: Annotated[ + str | None, + Field( + description="Announcement copy shown to users when the feature is introduced. Null when this feature is not in beta." + ), + ] = None + default_enabled: Annotated[ + bool, + Field( + alias="defaultEnabled", + description="Whether this feature is enabled by default.", + ), + ] + description: Annotated[ + str | None, + Field( + description="Short summary describing what the feature does. Null when this feature is not in beta." + ), + ] = None + display_name: Annotated[ + str | None, + Field( + alias="displayName", + description="User-facing display name shown in the experimental features UI. Null when this feature is not in beta.", + ), + ] = None + enabled: Annotated[ + bool, + Field(description="Whether this feature is currently enabled in the loaded config."), + ] + name: Annotated[str, Field(description="Stable key used in config.toml and CLI flag toggles.")] + stage: Annotated[ + ExperimentalFeatureStage, + Field(description="Lifecycle stage of this feature flag."), + ] + + +class ExperimentalFeatureListResponse(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + data: list[ExperimentalFeature] + next_cursor: Annotated[ + str | None, + Field( + alias="nextCursor", + description="Opaque cursor to pass to the next call to continue after the last item. If None, there are no more items to return.", + ), + ] = None + + +class ExternalAgentConfigMigrationItem(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + cwd: Annotated[ + str | None, + Field( + description="Null or empty means home-scoped migration; non-empty means repo-scoped migration." + ), + ] = None + description: str + item_type: Annotated[ExternalAgentConfigMigrationItemType, Field(alias="itemType")] + + +class FileUpdateChange(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + diff: str + kind: PatchChangeKind + path: str + + +class InputImageFunctionCallOutputContentItem(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + detail: ImageDetail | None = None + image_url: str + type: Annotated[ + Literal["input_image"], + Field(title="InputImageFunctionCallOutputContentItemType"), + ] + + +class FunctionCallOutputContentItem( + RootModel[InputTextFunctionCallOutputContentItem | InputImageFunctionCallOutputContentItem] +): + model_config = ConfigDict( + populate_by_name=True, + ) + root: Annotated[ + InputTextFunctionCallOutputContentItem | InputImageFunctionCallOutputContentItem, + Field( + description="Responses API compatible content items that can be returned by a tool call. This is a subset of ContentItem with the types we support as function call outputs." + ), + ] + + +class GetAccountResponse(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + account: Account | None = None + requires_openai_auth: Annotated[bool, Field(alias="requiresOpenaiAuth")] + + +class GuardianApprovalReview(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + rationale: str | None = None + risk_level: Annotated[GuardianRiskLevel | None, Field(alias="riskLevel")] = None + risk_score: Annotated[int | None, Field(alias="riskScore", ge=0)] = None + status: GuardianApprovalReviewStatus + + +class HookOutputEntry(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + kind: HookOutputEntryKind + text: str + + +class HookRunSummary(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + completed_at: Annotated[int | None, Field(alias="completedAt")] = None + display_order: Annotated[int, Field(alias="displayOrder")] + duration_ms: Annotated[int | None, Field(alias="durationMs")] = None + entries: list[HookOutputEntry] + event_name: Annotated[HookEventName, Field(alias="eventName")] + execution_mode: Annotated[HookExecutionMode, Field(alias="executionMode")] + handler_type: Annotated[HookHandlerType, Field(alias="handlerType")] + id: str + scope: HookScope + source_path: Annotated[str, Field(alias="sourcePath")] + started_at: Annotated[int, Field(alias="startedAt")] + status: HookRunStatus + status_message: Annotated[str | None, Field(alias="statusMessage")] = None + + +class HookStartedNotification(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + run: HookRunSummary + thread_id: Annotated[str, Field(alias="threadId")] + turn_id: Annotated[str | None, Field(alias="turnId")] = None + + +class ItemGuardianApprovalReviewCompletedNotification(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + action: Any | None = None + review: GuardianApprovalReview + target_item_id: Annotated[str, Field(alias="targetItemId")] + thread_id: Annotated[str, Field(alias="threadId")] + turn_id: Annotated[str, Field(alias="turnId")] + + +class ItemGuardianApprovalReviewStartedNotification(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + action: Any | None = None + review: GuardianApprovalReview + target_item_id: Annotated[str, Field(alias="targetItemId")] + thread_id: Annotated[str, Field(alias="threadId")] + turn_id: Annotated[str, Field(alias="turnId")] + + +class McpServerStatus(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + auth_status: Annotated[McpAuthStatus, Field(alias="authStatus")] + name: str + resource_templates: Annotated[list[ResourceTemplate], Field(alias="resourceTemplates")] + resources: list[Resource] + tools: dict[str, Tool] + + +class Model(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + availability_nux: Annotated[ModelAvailabilityNux | None, Field(alias="availabilityNux")] = None + default_reasoning_effort: Annotated[ReasoningEffort, Field(alias="defaultReasoningEffort")] + description: str + display_name: Annotated[str, Field(alias="displayName")] + hidden: bool + id: str + input_modalities: Annotated[list[InputModality] | None, Field(alias="inputModalities")] = [ + "text", + "image", + ] + is_default: Annotated[bool, Field(alias="isDefault")] + model: str + supported_reasoning_efforts: Annotated[ + list[ReasoningEffortOption], Field(alias="supportedReasoningEfforts") + ] + supports_personality: Annotated[bool | None, Field(alias="supportsPersonality")] = False + upgrade: str | None = None + upgrade_info: Annotated[ModelUpgradeInfo | None, Field(alias="upgradeInfo")] = None + + +class ModelListResponse(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + data: list[Model] + next_cursor: Annotated[ + str | None, + Field( + alias="nextCursor", + description="Opaque cursor to pass to the next call to continue after the last item. If None, there are no more items to return.", + ), + ] = None + + +class OverriddenMetadata(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + effective_value: Annotated[Any, Field(alias="effectiveValue")] + message: str + overriding_layer: Annotated[ConfigLayerMetadata, Field(alias="overridingLayer")] + + +class PluginDetail(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + apps: list[AppSummary] + description: str | None = None + marketplace_name: Annotated[str, Field(alias="marketplaceName")] + marketplace_path: Annotated[AbsolutePathBuf, Field(alias="marketplacePath")] + mcp_servers: Annotated[list[str], Field(alias="mcpServers")] + skills: list[SkillSummary] + summary: PluginSummary + + +class PluginMarketplaceEntry(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + name: str + path: AbsolutePathBuf + plugins: list[PluginSummary] + + +class PluginReadResponse(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + plugin: PluginDetail + + +class RateLimitSnapshot(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + credits: CreditsSnapshot | None = None + limit_id: Annotated[str | None, Field(alias="limitId")] = None + limit_name: Annotated[str | None, Field(alias="limitName")] = None + plan_type: Annotated[PlanType | None, Field(alias="planType")] = None + primary: RateLimitWindow | None = None + secondary: RateLimitWindow | None = None + + +class WebSearchCallResponseItem(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + action: ResponsesApiWebSearchAction | None = None + id: str | None = None + status: str | None = None + type: Annotated[Literal["web_search_call"], Field(title="WebSearchCallResponseItemType")] + + +class ReviewStartParams(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + delivery: Annotated[ + ReviewDelivery | None, + Field( + description="Where to run the review: inline (default) on the current thread or detached on a new thread (returned in `reviewThreadId`)." + ), + ] = None + target: ReviewTarget + thread_id: Annotated[str, Field(alias="threadId")] + + +class ErrorServerNotification(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + method: Annotated[Literal["error"], Field(title="ErrorNotificationMethod")] + params: ErrorNotification + + +class ThreadStatusChangedServerNotification(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + method: Annotated[ + Literal["thread/status/changed"], + Field(title="Thread/status/changedNotificationMethod"), + ] + params: ThreadStatusChangedNotification + + +class ThreadArchivedServerNotification(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + method: Annotated[Literal["thread/archived"], Field(title="Thread/archivedNotificationMethod")] + params: ThreadArchivedNotification + + +class ThreadUnarchivedServerNotification(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + method: Annotated[ + Literal["thread/unarchived"], Field(title="Thread/unarchivedNotificationMethod") + ] + params: ThreadUnarchivedNotification + + +class ThreadClosedServerNotification(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + method: Annotated[Literal["thread/closed"], Field(title="Thread/closedNotificationMethod")] + params: ThreadClosedNotification + + +class SkillsChangedServerNotification(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + method: Annotated[Literal["skills/changed"], Field(title="Skills/changedNotificationMethod")] + params: SkillsChangedNotification + + +class ThreadNameUpdatedServerNotification(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + method: Annotated[ + Literal["thread/name/updated"], + Field(title="Thread/name/updatedNotificationMethod"), + ] + params: ThreadNameUpdatedNotification + + +class HookStartedServerNotification(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + method: Annotated[Literal["hook/started"], Field(title="Hook/startedNotificationMethod")] + params: HookStartedNotification + + +class TurnDiffUpdatedServerNotification(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + method: Annotated[ + Literal["turn/diff/updated"], Field(title="Turn/diff/updatedNotificationMethod") + ] + params: TurnDiffUpdatedNotification + + +class ItemAutoApprovalReviewStartedServerNotification(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + method: Annotated[ + Literal["item/autoApprovalReview/started"], + Field(title="Item/autoApprovalReview/startedNotificationMethod"), + ] + params: ItemGuardianApprovalReviewStartedNotification + + +class ItemAutoApprovalReviewCompletedServerNotification(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + method: Annotated[ + Literal["item/autoApprovalReview/completed"], + Field(title="Item/autoApprovalReview/completedNotificationMethod"), + ] + params: ItemGuardianApprovalReviewCompletedNotification + + +class CommandExecOutputDeltaServerNotification(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + method: Annotated[ + Literal["command/exec/outputDelta"], + Field(title="Command/exec/outputDeltaNotificationMethod"), + ] + params: CommandExecOutputDeltaNotification + + +class ItemCommandExecutionTerminalInteractionServerNotification(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + method: Annotated[ + Literal["item/commandExecution/terminalInteraction"], + Field(title="Item/commandExecution/terminalInteractionNotificationMethod"), + ] + params: TerminalInteractionNotification + + +class ServerRequestResolvedServerNotification(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + method: Annotated[ + Literal["serverRequest/resolved"], + Field(title="ServerRequest/resolvedNotificationMethod"), + ] + params: ServerRequestResolvedNotification + + +class AccountUpdatedServerNotification(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + method: Annotated[Literal["account/updated"], Field(title="Account/updatedNotificationMethod")] + params: AccountUpdatedNotification + + +class ConfigWarningServerNotification(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + method: Annotated[Literal["configWarning"], Field(title="ConfigWarningNotificationMethod")] + params: ConfigWarningNotification + + +class ThreadRealtimeStartedServerNotification(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + method: Annotated[ + Literal["thread/realtime/started"], + Field(title="Thread/realtime/startedNotificationMethod"), + ] + params: ThreadRealtimeStartedNotification + + +class ThreadRealtimeItemAddedServerNotification(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + method: Annotated[ + Literal["thread/realtime/itemAdded"], + Field(title="Thread/realtime/itemAddedNotificationMethod"), + ] + params: ThreadRealtimeItemAddedNotification + + +class ThreadRealtimeOutputAudioDeltaServerNotification(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + method: Annotated[ + Literal["thread/realtime/outputAudio/delta"], + Field(title="Thread/realtime/outputAudio/deltaNotificationMethod"), + ] + params: ThreadRealtimeOutputAudioDeltaNotification + + +class ThreadRealtimeErrorServerNotification(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + method: Annotated[ + Literal["thread/realtime/error"], + Field(title="Thread/realtime/errorNotificationMethod"), + ] + params: ThreadRealtimeErrorNotification + + +class ThreadRealtimeClosedServerNotification(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + method: Annotated[ + Literal["thread/realtime/closed"], + Field(title="Thread/realtime/closedNotificationMethod"), + ] + params: ThreadRealtimeClosedNotification + + +class WindowsWorldWritableWarningServerNotification(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + method: Annotated[ + Literal["windows/worldWritableWarning"], + Field(title="Windows/worldWritableWarningNotificationMethod"), + ] + params: WindowsWorldWritableWarningNotification + + +class SkillDependencies(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + tools: list[SkillToolDependency] + + +class SkillMetadata(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + dependencies: SkillDependencies | None = None + description: str + enabled: bool + interface: SkillInterface | None = None + name: str + path: str + scope: SkillScope + short_description: Annotated[ + str | None, + Field( + alias="shortDescription", + description="Legacy short_description from SKILL.md. Prefer SKILL.json interface.short_description.", + ), + ] = None + + +class SkillsListEntry(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + cwd: str + errors: list[SkillErrorInfo] + skills: list[SkillMetadata] + + +class SkillsListResponse(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + data: list[SkillsListEntry] + + +class ThreadSpawn(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + agent_nickname: str | None = None + agent_role: str | None = None + depth: int + parent_thread_id: ThreadId + + +class ThreadSpawnSubAgentSource(BaseModel): + model_config = ConfigDict( + extra="forbid", + populate_by_name=True, + ) + thread_spawn: ThreadSpawn + + +class SubAgentSource( + RootModel[SubAgentSourceValue | ThreadSpawnSubAgentSource | OtherSubAgentSource] +): + model_config = ConfigDict( + populate_by_name=True, + ) + root: SubAgentSourceValue | ThreadSpawnSubAgentSource | OtherSubAgentSource + + +class UserMessageThreadItem(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + content: list[UserInput] + id: str + type: Annotated[Literal["userMessage"], Field(title="UserMessageThreadItemType")] + + +class FileChangeThreadItem(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + changes: list[FileUpdateChange] + id: str + status: PatchApplyStatus + type: Annotated[Literal["fileChange"], Field(title="FileChangeThreadItemType")] + + +class CollabAgentToolCallThreadItem(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + agents_states: Annotated[ + dict[str, CollabAgentState], + Field( + alias="agentsStates", + description="Last known status of the target agents, when available.", + ), + ] + id: Annotated[str, Field(description="Unique identifier for this collab tool call.")] + model: Annotated[ + str | None, + Field(description="Model requested for the spawned agent, when applicable."), + ] = None + prompt: Annotated[ + str | None, + Field(description="Prompt text sent as part of the collab tool call, when available."), + ] = None + reasoning_effort: Annotated[ + ReasoningEffort | None, + Field( + alias="reasoningEffort", + description="Reasoning effort requested for the spawned agent, when applicable.", + ), + ] = None + receiver_thread_ids: Annotated[ + list[str], + Field( + alias="receiverThreadIds", + description="Thread ID of the receiving agent, when applicable. In case of spawn operation, this corresponds to the newly spawned agent.", + ), + ] + sender_thread_id: Annotated[ + str, + Field( + alias="senderThreadId", + description="Thread ID of the agent issuing the collab request.", + ), + ] + status: Annotated[ + CollabAgentToolCallStatus, + Field(description="Current status of the collab tool call."), + ] + tool: Annotated[CollabAgentTool, Field(description="Name of the collab tool that was invoked.")] + type: Annotated[ + Literal["collabAgentToolCall"], Field(title="CollabAgentToolCallThreadItemType") + ] + + +class WebSearchThreadItem(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + action: WebSearchAction | None = None + id: str + query: str + type: Annotated[Literal["webSearch"], Field(title="WebSearchThreadItemType")] + + +class ThreadItem( + RootModel[ + UserMessageThreadItem + | AgentMessageThreadItem + | PlanThreadItem + | ReasoningThreadItem + | CommandExecutionThreadItem + | FileChangeThreadItem + | McpToolCallThreadItem + | DynamicToolCallThreadItem + | CollabAgentToolCallThreadItem + | WebSearchThreadItem + | ImageViewThreadItem + | ImageGenerationThreadItem + | EnteredReviewModeThreadItem + | ExitedReviewModeThreadItem + | ContextCompactionThreadItem + ] +): + model_config = ConfigDict( + populate_by_name=True, + ) + root: ( + UserMessageThreadItem + | AgentMessageThreadItem + | PlanThreadItem + | ReasoningThreadItem + | CommandExecutionThreadItem + | FileChangeThreadItem + | McpToolCallThreadItem + | DynamicToolCallThreadItem + | CollabAgentToolCallThreadItem + | WebSearchThreadItem + | ImageViewThreadItem + | ImageGenerationThreadItem + | EnteredReviewModeThreadItem + | ExitedReviewModeThreadItem + | ContextCompactionThreadItem + ) + + +class ThreadListParams(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + archived: Annotated[ + bool | None, + Field( + description="Optional archived filter; when set to true, only archived threads are returned. If false or null, only non-archived threads are returned." + ), + ] = None + cursor: Annotated[ + str | None, + Field(description="Opaque pagination cursor returned by a previous call."), + ] = None + cwd: Annotated[ + str | None, + Field( + description="Optional cwd filter; when set, only threads whose session cwd exactly matches this path are returned." + ), + ] = None + limit: Annotated[ + int | None, + Field( + description="Optional page size; defaults to a reasonable server-side value.", + ge=0, + ), + ] = None + model_providers: Annotated[ + list[str] | None, + Field( + alias="modelProviders", + description="Optional provider filter; when set, only sessions recorded under these providers are returned. When present but empty, includes all providers.", + ), + ] = None + search_term: Annotated[ + str | None, + Field( + alias="searchTerm", + description="Optional substring filter for the extracted thread title.", + ), + ] = None + sort_key: Annotated[ + ThreadSortKey | None, + Field(alias="sortKey", description="Optional sort key; defaults to created_at."), + ] = None + source_kinds: Annotated[ + list[ThreadSourceKind] | None, + Field( + alias="sourceKinds", + description="Optional source filter; when set, only sessions from these source kinds are returned. When omitted or empty, defaults to interactive sources.", + ), + ] = None + + +class ThreadTokenUsage(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + last: TokenUsageBreakdown + model_context_window: Annotated[int | None, Field(alias="modelContextWindow")] = None + total: TokenUsageBreakdown + + +class ThreadTokenUsageUpdatedNotification(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + thread_id: Annotated[str, Field(alias="threadId")] + token_usage: Annotated[ThreadTokenUsage, Field(alias="tokenUsage")] + turn_id: Annotated[str, Field(alias="turnId")] + + +class ThreadUnsubscribeResponse(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + status: ThreadUnsubscribeStatus + + +class ToolsV2(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + view_image: bool | None = None + web_search: WebSearchToolConfig | None = None + + +class Turn(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + error: Annotated[ + TurnError | None, + Field(description="Only populated when the Turn's status is failed."), + ] = None + id: str + items: Annotated[ + list[ThreadItem], + Field( + description="Only populated on a `thread/resume` or `thread/fork` response. For all other responses and notifications returning a Turn, the items field will be an empty list." + ), + ] + status: TurnStatus + + +class TurnCompletedNotification(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + thread_id: Annotated[str, Field(alias="threadId")] + turn: Turn + + +class TurnPlanStep(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + status: TurnPlanStepStatus + step: str + + +class TurnPlanUpdatedNotification(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + explanation: str | None = None + plan: list[TurnPlanStep] + thread_id: Annotated[str, Field(alias="threadId")] + turn_id: Annotated[str, Field(alias="turnId")] + + +class TurnStartParams(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + approval_policy: Annotated[ + AskForApproval | None, + Field( + alias="approvalPolicy", + description="Override the approval policy for this turn and subsequent turns.", + ), + ] = None + approvals_reviewer: Annotated[ + ApprovalsReviewer | None, + Field( + alias="approvalsReviewer", + description="Override where approval requests are routed for review on this turn and subsequent turns.", + ), + ] = None + cwd: Annotated[ + str | None, + Field(description="Override the working directory for this turn and subsequent turns."), + ] = None + effort: Annotated[ + ReasoningEffort | None, + Field(description="Override the reasoning effort for this turn and subsequent turns."), + ] = None + input: list[UserInput] + model: Annotated[ + str | None, + Field(description="Override the model for this turn and subsequent turns."), + ] = None + output_schema: Annotated[ + Any | None, + Field( + alias="outputSchema", + description="Optional JSON Schema used to constrain the final assistant message for this turn.", + ), + ] = None + personality: Annotated[ + Personality | None, + Field(description="Override the personality for this turn and subsequent turns."), + ] = None + sandbox_policy: Annotated[ + SandboxPolicy | None, + Field( + alias="sandboxPolicy", + description="Override the sandbox policy for this turn and subsequent turns.", + ), + ] = None + service_tier: Annotated[ + ServiceTier | None, + Field( + alias="serviceTier", + description="Override the service tier for this turn and subsequent turns.", + ), + ] = None + summary: Annotated[ + ReasoningSummary | None, + Field(description="Override the reasoning summary for this turn and subsequent turns."), + ] = None + thread_id: Annotated[str, Field(alias="threadId")] + + +class TurnStartResponse(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + turn: Turn + + +class TurnStartedNotification(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + thread_id: Annotated[str, Field(alias="threadId")] + turn: Turn + + +class TurnSteerParams(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + expected_turn_id: Annotated[ + str, + Field( + alias="expectedTurnId", + description="Required active turn id precondition. The request fails when it does not match the currently active turn.", + ), + ] + input: list[UserInput] + thread_id: Annotated[str, Field(alias="threadId")] + + +class WindowsSandboxSetupCompletedNotification(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + error: str | None = None + mode: WindowsSandboxSetupMode + success: bool + + +class AccountRateLimitsUpdatedNotification(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + rate_limits: Annotated[RateLimitSnapshot, Field(alias="rateLimits")] + + +class AppInfo(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + app_metadata: Annotated[AppMetadata | None, Field(alias="appMetadata")] = None + branding: AppBranding | None = None + description: str | None = None + distribution_channel: Annotated[str | None, Field(alias="distributionChannel")] = None + id: str + install_url: Annotated[str | None, Field(alias="installUrl")] = None + is_accessible: Annotated[bool | None, Field(alias="isAccessible")] = False + is_enabled: Annotated[ + bool | None, + Field( + alias="isEnabled", + description="Whether this app is enabled in config.toml. Example: ```toml [apps.bad_app] enabled = false ```", + ), + ] = True + labels: dict[str, Any] | None = None + logo_url: Annotated[str | None, Field(alias="logoUrl")] = None + logo_url_dark: Annotated[str | None, Field(alias="logoUrlDark")] = None + name: str + plugin_display_names: Annotated[list[str] | None, Field(alias="pluginDisplayNames")] = [] + + +class AppListUpdatedNotification(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + data: list[AppInfo] + + +class AppsListResponse(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + data: list[AppInfo] + next_cursor: Annotated[ + str | None, + Field( + alias="nextCursor", + description="Opaque cursor to pass to the next call to continue after the last item. If None, there are no more items to return.", + ), + ] = None + + +class ThreadListRequest(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + id: RequestId + method: Annotated[Literal["thread/list"], Field(title="Thread/listRequestMethod")] + params: ThreadListParams + + +class TurnStartRequest(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + id: RequestId + method: Annotated[Literal["turn/start"], Field(title="Turn/startRequestMethod")] + params: TurnStartParams + + +class TurnSteerRequest(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + id: RequestId + method: Annotated[Literal["turn/steer"], Field(title="Turn/steerRequestMethod")] + params: TurnSteerParams + + +class ReviewStartRequest(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + id: RequestId + method: Annotated[Literal["review/start"], Field(title="Review/startRequestMethod")] + params: ReviewStartParams + + +class CommandExecRequest(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + id: RequestId + method: Annotated[Literal["command/exec"], Field(title="Command/execRequestMethod")] + params: CommandExecParams + + +class CommandExecResizeRequest(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + id: RequestId + method: Annotated[ + Literal["command/exec/resize"], Field(title="Command/exec/resizeRequestMethod") + ] + params: CommandExecResizeParams + + +class ConfigValueWriteRequest(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + id: RequestId + method: Annotated[Literal["config/value/write"], Field(title="Config/value/writeRequestMethod")] + params: ConfigValueWriteParams + + +class ConfigBatchWriteParams(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + edits: list[ConfigEdit] + expected_version: Annotated[str | None, Field(alias="expectedVersion")] = None + file_path: Annotated[ + str | None, + Field( + alias="filePath", + description="Path to the config file to write; defaults to the user's `config.toml` when omitted.", + ), + ] = None + reload_user_config: Annotated[ + bool | None, + Field( + alias="reloadUserConfig", + description="When true, hot-reload the updated user config into all loaded threads after writing.", + ), + ] = None + + +class ConfigWriteResponse(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + file_path: Annotated[ + AbsolutePathBuf, + Field( + alias="filePath", + description="Canonical path to the config file that was written.", + ), + ] + overridden_metadata: Annotated[OverriddenMetadata | None, Field(alias="overriddenMetadata")] = ( + None + ) + status: WriteStatus + version: str + + +class ExternalAgentConfigDetectResponse(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + items: list[ExternalAgentConfigMigrationItem] + + +class ExternalAgentConfigImportParams(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + migration_items: Annotated[ + list[ExternalAgentConfigMigrationItem], Field(alias="migrationItems") + ] + + +class FunctionCallOutputBody(RootModel[str | list[FunctionCallOutputContentItem]]): + model_config = ConfigDict( + populate_by_name=True, + ) + root: str | list[FunctionCallOutputContentItem] + + +class FunctionCallOutputPayload(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + body: FunctionCallOutputBody + success: bool | None = None + + +class GetAccountRateLimitsResponse(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + rate_limits: Annotated[ + RateLimitSnapshot, + Field( + alias="rateLimits", + description="Backward-compatible single-bucket view; mirrors the historical payload.", + ), + ] + rate_limits_by_limit_id: Annotated[ + dict[str, Any] | None, + Field( + alias="rateLimitsByLimitId", + description="Multi-bucket view keyed by metered `limit_id` (for example, `codex`).", + ), + ] = None + + +class HookCompletedNotification(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + run: HookRunSummary + thread_id: Annotated[str, Field(alias="threadId")] + turn_id: Annotated[str | None, Field(alias="turnId")] = None + + +class ItemCompletedNotification(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + item: ThreadItem + thread_id: Annotated[str, Field(alias="threadId")] + turn_id: Annotated[str, Field(alias="turnId")] + + +class ItemStartedNotification(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + item: ThreadItem + thread_id: Annotated[str, Field(alias="threadId")] + turn_id: Annotated[str, Field(alias="turnId")] + + +class ListMcpServerStatusResponse(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + data: list[McpServerStatus] + next_cursor: Annotated[ + str | None, + Field( + alias="nextCursor", + description="Opaque cursor to pass to the next call to continue after the last item. If None, there are no more items to return.", + ), + ] = None + + +class PluginListResponse(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + marketplaces: list[PluginMarketplaceEntry] + remote_sync_error: Annotated[str | None, Field(alias="remoteSyncError")] = None + + +class ProfileV2(BaseModel): + model_config = ConfigDict( + extra="allow", + populate_by_name=True, + ) + approval_policy: AskForApproval | None = None + approvals_reviewer: Annotated[ + ApprovalsReviewer | None, + Field( + description="[UNSTABLE] Optional profile-level override for where approval requests are routed for review. If omitted, the enclosing config default is used." + ), + ] = None + chatgpt_base_url: str | None = None + model: str | None = None + model_provider: str | None = None + model_reasoning_effort: ReasoningEffort | None = None + model_reasoning_summary: ReasoningSummary | None = None + model_verbosity: Verbosity | None = None + service_tier: ServiceTier | None = None + tools: ToolsV2 | None = None + web_search: WebSearchMode | None = None + + +class FunctionCallOutputResponseItem(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + call_id: str + output: FunctionCallOutputPayload + type: Annotated[ + Literal["function_call_output"], + Field(title="FunctionCallOutputResponseItemType"), + ] + + +class CustomToolCallOutputResponseItem(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + call_id: str + output: FunctionCallOutputPayload + type: Annotated[ + Literal["custom_tool_call_output"], + Field(title="CustomToolCallOutputResponseItemType"), + ] + + +class ResponseItem( + RootModel[ + MessageResponseItem + | ReasoningResponseItem + | LocalShellCallResponseItem + | FunctionCallResponseItem + | ToolSearchCallResponseItem + | FunctionCallOutputResponseItem + | CustomToolCallResponseItem + | CustomToolCallOutputResponseItem + | ToolSearchOutputResponseItem + | WebSearchCallResponseItem + | ImageGenerationCallResponseItem + | GhostSnapshotResponseItem + | CompactionResponseItem + | OtherResponseItem + ] +): + model_config = ConfigDict( + populate_by_name=True, + ) + root: ( + MessageResponseItem + | ReasoningResponseItem + | LocalShellCallResponseItem + | FunctionCallResponseItem + | ToolSearchCallResponseItem + | FunctionCallOutputResponseItem + | CustomToolCallResponseItem + | CustomToolCallOutputResponseItem + | ToolSearchOutputResponseItem + | WebSearchCallResponseItem + | ImageGenerationCallResponseItem + | GhostSnapshotResponseItem + | CompactionResponseItem + | OtherResponseItem + ) + + +class ReviewStartResponse(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + review_thread_id: Annotated[ + str, + Field( + alias="reviewThreadId", + description="Identifies the thread where the review runs.\n\nFor inline reviews, this is the original thread id. For detached reviews, this is the id of the new review thread.", + ), + ] + turn: Turn + + +class ThreadTokenUsageUpdatedServerNotification(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + method: Annotated[ + Literal["thread/tokenUsage/updated"], + Field(title="Thread/tokenUsage/updatedNotificationMethod"), + ] + params: ThreadTokenUsageUpdatedNotification + + +class TurnStartedServerNotification(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + method: Annotated[Literal["turn/started"], Field(title="Turn/startedNotificationMethod")] + params: TurnStartedNotification + + +class TurnCompletedServerNotification(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + method: Annotated[Literal["turn/completed"], Field(title="Turn/completedNotificationMethod")] + params: TurnCompletedNotification + + +class HookCompletedServerNotification(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + method: Annotated[Literal["hook/completed"], Field(title="Hook/completedNotificationMethod")] + params: HookCompletedNotification + + +class TurnPlanUpdatedServerNotification(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + method: Annotated[ + Literal["turn/plan/updated"], Field(title="Turn/plan/updatedNotificationMethod") + ] + params: TurnPlanUpdatedNotification + + +class ItemStartedServerNotification(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + method: Annotated[Literal["item/started"], Field(title="Item/startedNotificationMethod")] + params: ItemStartedNotification + + +class ItemCompletedServerNotification(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + method: Annotated[Literal["item/completed"], Field(title="Item/completedNotificationMethod")] + params: ItemCompletedNotification + + +class AccountRateLimitsUpdatedServerNotification(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + method: Annotated[ + Literal["account/rateLimits/updated"], + Field(title="Account/rateLimits/updatedNotificationMethod"), + ] + params: AccountRateLimitsUpdatedNotification + + +class AppListUpdatedServerNotification(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + method: Annotated[ + Literal["app/list/updated"], Field(title="App/list/updatedNotificationMethod") + ] + params: AppListUpdatedNotification + + +class WindowsSandboxSetupCompletedServerNotification(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + method: Annotated[ + Literal["windowsSandbox/setupCompleted"], + Field(title="WindowsSandbox/setupCompletedNotificationMethod"), + ] + params: WindowsSandboxSetupCompletedNotification + + +class SubAgentSessionSource(BaseModel): + model_config = ConfigDict( + extra="forbid", + populate_by_name=True, + ) + sub_agent: Annotated[SubAgentSource, Field(alias="subAgent")] + + +class SessionSource(RootModel[SessionSourceValue | SubAgentSessionSource]): + model_config = ConfigDict( + populate_by_name=True, + ) + root: SessionSourceValue | SubAgentSessionSource + + +class Thread(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + agent_nickname: Annotated[ + str | None, + Field( + alias="agentNickname", + description="Optional random unique nickname assigned to an AgentControl-spawned sub-agent.", + ), + ] = None + agent_role: Annotated[ + str | None, + Field( + alias="agentRole", + description="Optional role (agent_role) assigned to an AgentControl-spawned sub-agent.", + ), + ] = None + cli_version: Annotated[ + str, + Field( + alias="cliVersion", + description="Version of the CLI that created the thread.", + ), + ] + created_at: Annotated[ + int, + Field( + alias="createdAt", + description="Unix timestamp (in seconds) when the thread was created.", + ), + ] + cwd: Annotated[str, Field(description="Working directory captured for the thread.")] + ephemeral: Annotated[ + bool, + Field( + description="Whether the thread is ephemeral and should not be materialized on disk." + ), + ] + git_info: Annotated[ + GitInfo | None, + Field( + alias="gitInfo", + description="Optional Git metadata captured when the thread was created.", + ), + ] = None + id: str + model_provider: Annotated[ + str, + Field( + alias="modelProvider", + description="Model provider used for this thread (for example, 'openai').", + ), + ] + name: Annotated[str | None, Field(description="Optional user-facing thread title.")] = None + path: Annotated[str | None, Field(description="[UNSTABLE] Path to the thread on disk.")] = None + preview: Annotated[ + str, + Field(description="Usually the first user message in the thread, if available."), + ] + source: Annotated[ + SessionSource, + Field( + description="Origin of the thread (CLI, VSCode, codex exec, codex app-server, etc.)." + ), + ] + status: Annotated[ThreadStatus, Field(description="Current runtime status for the thread.")] + turns: Annotated[ + list[Turn], + Field( + description="Only populated on `thread/resume`, `thread/rollback`, `thread/fork`, and `thread/read` (when `includeTurns` is true) responses. For all other responses and notifications returning a Thread, the turns field will be an empty list." + ), + ] + updated_at: Annotated[ + int, + Field( + alias="updatedAt", + description="Unix timestamp (in seconds) when the thread was last updated.", + ), + ] + + +class ThreadForkResponse(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + approval_policy: Annotated[AskForApproval, Field(alias="approvalPolicy")] + approvals_reviewer: Annotated[ + ApprovalsReviewer, + Field( + alias="approvalsReviewer", + description="Reviewer currently used for approval requests on this thread.", + ), + ] + cwd: str + model: str + model_provider: Annotated[str, Field(alias="modelProvider")] + reasoning_effort: Annotated[ReasoningEffort | None, Field(alias="reasoningEffort")] = None + sandbox: SandboxPolicy + service_tier: Annotated[ServiceTier | None, Field(alias="serviceTier")] = None + thread: Thread + + +class ThreadListResponse(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + data: list[Thread] + next_cursor: Annotated[ + str | None, + Field( + alias="nextCursor", + description="Opaque cursor to pass to the next call to continue after the last item. if None, there are no more items to return.", + ), + ] = None + + +class ThreadMetadataUpdateResponse(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + thread: Thread + + +class ThreadReadResponse(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + thread: Thread + + +class ThreadResumeResponse(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + approval_policy: Annotated[AskForApproval, Field(alias="approvalPolicy")] + approvals_reviewer: Annotated[ + ApprovalsReviewer, + Field( + alias="approvalsReviewer", + description="Reviewer currently used for approval requests on this thread.", + ), + ] + cwd: str + model: str + model_provider: Annotated[str, Field(alias="modelProvider")] + reasoning_effort: Annotated[ReasoningEffort | None, Field(alias="reasoningEffort")] = None + sandbox: SandboxPolicy + service_tier: Annotated[ServiceTier | None, Field(alias="serviceTier")] = None + thread: Thread + + +class ThreadRollbackResponse(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + thread: Annotated[ + Thread, + Field( + description="The updated thread after applying the rollback, with `turns` populated.\n\nThe ThreadItems stored in each Turn are lossy since we explicitly do not persist all agent interactions, such as command executions. This is the same behavior as `thread/resume`." + ), + ] + + +class ThreadStartResponse(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + approval_policy: Annotated[AskForApproval, Field(alias="approvalPolicy")] + approvals_reviewer: Annotated[ + ApprovalsReviewer, + Field( + alias="approvalsReviewer", + description="Reviewer currently used for approval requests on this thread.", + ), + ] + cwd: str + model: str + model_provider: Annotated[str, Field(alias="modelProvider")] + reasoning_effort: Annotated[ReasoningEffort | None, Field(alias="reasoningEffort")] = None + sandbox: SandboxPolicy + service_tier: Annotated[ServiceTier | None, Field(alias="serviceTier")] = None + thread: Thread + + +class ThreadStartedNotification(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + thread: Thread + + +class ThreadUnarchiveResponse(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + thread: Thread + + +class ExternalAgentConfigImportRequest(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + id: RequestId + method: Annotated[ + Literal["externalAgentConfig/import"], + Field(title="ExternalAgentConfig/importRequestMethod"), + ] + params: ExternalAgentConfigImportParams + + +class ConfigBatchWriteRequest(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + id: RequestId + method: Annotated[Literal["config/batchWrite"], Field(title="Config/batchWriteRequestMethod")] + params: ConfigBatchWriteParams + + +class ClientRequest( + RootModel[ + InitializeRequest + | ThreadStartRequest + | ThreadResumeRequest + | ThreadForkRequest + | ThreadArchiveRequest + | ThreadUnsubscribeRequest + | ThreadNameSetRequest + | ThreadMetadataUpdateRequest + | ThreadUnarchiveRequest + | ThreadCompactStartRequest + | ThreadRollbackRequest + | ThreadListRequest + | ThreadLoadedListRequest + | ThreadReadRequest + | SkillsListRequest + | PluginListRequest + | PluginReadRequest + | SkillsRemoteListRequest + | SkillsRemoteExportRequest + | AppListRequest + | FsReadFileRequest + | FsWriteFileRequest + | FsCreateDirectoryRequest + | FsGetMetadataRequest + | FsReadDirectoryRequest + | FsRemoveRequest + | FsCopyRequest + | SkillsConfigWriteRequest + | PluginInstallRequest + | PluginUninstallRequest + | TurnStartRequest + | TurnSteerRequest + | TurnInterruptRequest + | ReviewStartRequest + | ModelListRequest + | ExperimentalFeatureListRequest + | McpServerOauthLoginRequest + | ConfigMcpServerReloadRequest + | McpServerStatusListRequest + | WindowsSandboxSetupStartRequest + | AccountLoginStartRequest + | AccountLoginCancelRequest + | AccountLogoutRequest + | AccountRateLimitsReadRequest + | FeedbackUploadRequest + | CommandExecRequest + | CommandExecWriteRequest + | CommandExecTerminateRequest + | CommandExecResizeRequest + | ConfigReadRequest + | ExternalAgentConfigDetectRequest + | ExternalAgentConfigImportRequest + | ConfigValueWriteRequest + | ConfigBatchWriteRequest + | ConfigRequirementsReadRequest + | AccountReadRequest + | FuzzyFileSearchRequest + ] +): + model_config = ConfigDict( + populate_by_name=True, + ) + root: Annotated[ + InitializeRequest + | ThreadStartRequest + | ThreadResumeRequest + | ThreadForkRequest + | ThreadArchiveRequest + | ThreadUnsubscribeRequest + | ThreadNameSetRequest + | ThreadMetadataUpdateRequest + | ThreadUnarchiveRequest + | ThreadCompactStartRequest + | ThreadRollbackRequest + | ThreadListRequest + | ThreadLoadedListRequest + | ThreadReadRequest + | SkillsListRequest + | PluginListRequest + | PluginReadRequest + | SkillsRemoteListRequest + | SkillsRemoteExportRequest + | AppListRequest + | FsReadFileRequest + | FsWriteFileRequest + | FsCreateDirectoryRequest + | FsGetMetadataRequest + | FsReadDirectoryRequest + | FsRemoveRequest + | FsCopyRequest + | SkillsConfigWriteRequest + | PluginInstallRequest + | PluginUninstallRequest + | TurnStartRequest + | TurnSteerRequest + | TurnInterruptRequest + | ReviewStartRequest + | ModelListRequest + | ExperimentalFeatureListRequest + | McpServerOauthLoginRequest + | ConfigMcpServerReloadRequest + | McpServerStatusListRequest + | WindowsSandboxSetupStartRequest + | AccountLoginStartRequest + | AccountLoginCancelRequest + | AccountLogoutRequest + | AccountRateLimitsReadRequest + | FeedbackUploadRequest + | CommandExecRequest + | CommandExecWriteRequest + | CommandExecTerminateRequest + | CommandExecResizeRequest + | ConfigReadRequest + | ExternalAgentConfigDetectRequest + | ExternalAgentConfigImportRequest + | ConfigValueWriteRequest + | ConfigBatchWriteRequest + | ConfigRequirementsReadRequest + | AccountReadRequest + | FuzzyFileSearchRequest, + Field(description="Request from the client to the server.", title="ClientRequest"), + ] + + +class Config(BaseModel): + model_config = ConfigDict( + extra="allow", + populate_by_name=True, + ) + analytics: AnalyticsConfig | None = None + approval_policy: AskForApproval | None = None + approvals_reviewer: Annotated[ + ApprovalsReviewer | None, + Field( + description="[UNSTABLE] Optional default for where approval requests are routed for review." + ), + ] = None + compact_prompt: str | None = None + developer_instructions: str | None = None + forced_chatgpt_workspace_id: str | None = None + forced_login_method: ForcedLoginMethod | None = None + instructions: str | None = None + model: str | None = None + model_auto_compact_token_limit: int | None = None + model_context_window: int | None = None + model_provider: str | None = None + model_reasoning_effort: ReasoningEffort | None = None + model_reasoning_summary: ReasoningSummary | None = None + model_verbosity: Verbosity | None = None + profile: str | None = None + profiles: dict[str, ProfileV2] | None = {} + review_model: str | None = None + sandbox_mode: SandboxMode | None = None + sandbox_workspace_write: SandboxWorkspaceWrite | None = None + service_tier: ServiceTier | None = None + tools: ToolsV2 | None = None + web_search: WebSearchMode | None = None + + +class ConfigReadResponse(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + config: Config + layers: list[ConfigLayer] | None = None + origins: dict[str, ConfigLayerMetadata] + + +class RawResponseItemCompletedNotification(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + item: ResponseItem + thread_id: Annotated[str, Field(alias="threadId")] + turn_id: Annotated[str, Field(alias="turnId")] + + +class ThreadStartedServerNotification(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + method: Annotated[Literal["thread/started"], Field(title="Thread/startedNotificationMethod")] + params: ThreadStartedNotification + + +class ServerNotification( + RootModel[ + ErrorServerNotification + | ThreadStartedServerNotification + | ThreadStatusChangedServerNotification + | ThreadArchivedServerNotification + | ThreadUnarchivedServerNotification + | ThreadClosedServerNotification + | SkillsChangedServerNotification + | ThreadNameUpdatedServerNotification + | ThreadTokenUsageUpdatedServerNotification + | TurnStartedServerNotification + | HookStartedServerNotification + | TurnCompletedServerNotification + | HookCompletedServerNotification + | TurnDiffUpdatedServerNotification + | TurnPlanUpdatedServerNotification + | ItemStartedServerNotification + | ItemAutoApprovalReviewStartedServerNotification + | ItemAutoApprovalReviewCompletedServerNotification + | ItemCompletedServerNotification + | ItemAgentMessageDeltaServerNotification + | ItemPlanDeltaServerNotification + | CommandExecOutputDeltaServerNotification + | ItemCommandExecutionOutputDeltaServerNotification + | ItemCommandExecutionTerminalInteractionServerNotification + | ItemFileChangeOutputDeltaServerNotification + | ServerRequestResolvedServerNotification + | ItemMcpToolCallProgressServerNotification + | McpServerOauthLoginCompletedServerNotification + | AccountUpdatedServerNotification + | AccountRateLimitsUpdatedServerNotification + | AppListUpdatedServerNotification + | ItemReasoningSummaryTextDeltaServerNotification + | ItemReasoningSummaryPartAddedServerNotification + | ItemReasoningTextDeltaServerNotification + | ThreadCompactedServerNotification + | ModelReroutedServerNotification + | DeprecationNoticeServerNotification + | ConfigWarningServerNotification + | FuzzyFileSearchSessionUpdatedServerNotification + | FuzzyFileSearchSessionCompletedServerNotification + | ThreadRealtimeStartedServerNotification + | ThreadRealtimeItemAddedServerNotification + | ThreadRealtimeOutputAudioDeltaServerNotification + | ThreadRealtimeErrorServerNotification + | ThreadRealtimeClosedServerNotification + | WindowsWorldWritableWarningServerNotification + | WindowsSandboxSetupCompletedServerNotification + | AccountLoginCompletedServerNotification + ] +): + model_config = ConfigDict( + populate_by_name=True, + ) + root: Annotated[ + ErrorServerNotification + | ThreadStartedServerNotification + | ThreadStatusChangedServerNotification + | ThreadArchivedServerNotification + | ThreadUnarchivedServerNotification + | ThreadClosedServerNotification + | SkillsChangedServerNotification + | ThreadNameUpdatedServerNotification + | ThreadTokenUsageUpdatedServerNotification + | TurnStartedServerNotification + | HookStartedServerNotification + | TurnCompletedServerNotification + | HookCompletedServerNotification + | TurnDiffUpdatedServerNotification + | TurnPlanUpdatedServerNotification + | ItemStartedServerNotification + | ItemAutoApprovalReviewStartedServerNotification + | ItemAutoApprovalReviewCompletedServerNotification + | ItemCompletedServerNotification + | ItemAgentMessageDeltaServerNotification + | ItemPlanDeltaServerNotification + | CommandExecOutputDeltaServerNotification + | ItemCommandExecutionOutputDeltaServerNotification + | ItemCommandExecutionTerminalInteractionServerNotification + | ItemFileChangeOutputDeltaServerNotification + | ServerRequestResolvedServerNotification + | ItemMcpToolCallProgressServerNotification + | McpServerOauthLoginCompletedServerNotification + | AccountUpdatedServerNotification + | AccountRateLimitsUpdatedServerNotification + | AppListUpdatedServerNotification + | ItemReasoningSummaryTextDeltaServerNotification + | ItemReasoningSummaryPartAddedServerNotification + | ItemReasoningTextDeltaServerNotification + | ThreadCompactedServerNotification + | ModelReroutedServerNotification + | DeprecationNoticeServerNotification + | ConfigWarningServerNotification + | FuzzyFileSearchSessionUpdatedServerNotification + | FuzzyFileSearchSessionCompletedServerNotification + | ThreadRealtimeStartedServerNotification + | ThreadRealtimeItemAddedServerNotification + | ThreadRealtimeOutputAudioDeltaServerNotification + | ThreadRealtimeErrorServerNotification + | ThreadRealtimeClosedServerNotification + | WindowsWorldWritableWarningServerNotification + | WindowsSandboxSetupCompletedServerNotification + | AccountLoginCompletedServerNotification, + Field( + description="Notification sent from the server to the client.", + title="ServerNotification", + ), + ] diff --git a/src/agents/sandbox/app_server/generated/v2_types.py b/src/agents/sandbox/app_server/generated/v2_types.py new file mode 100644 index 0000000000..2ddf81fb7b --- /dev/null +++ b/src/agents/sandbox/app_server/generated/v2_types.py @@ -0,0 +1,23 @@ +"""Stable aliases over full v2 autogenerated models (datamodel-code-generator).""" + +from .v2_all import ( + ModelListResponse, + ThreadCompactStartResponse, + ThreadItem, + ThreadListResponse, + ThreadReadResponse, + ThreadTokenUsageUpdatedNotification, + TurnCompletedNotification as TurnCompletedNotificationPayload, + TurnSteerResponse, +) + +__all__ = [ + "ModelListResponse", + "ThreadCompactStartResponse", + "ThreadListResponse", + "ThreadReadResponse", + "ThreadTokenUsageUpdatedNotification", + "TurnCompletedNotificationPayload", + "TurnSteerResponse", + "ThreadItem", +] diff --git a/src/agents/sandbox/app_server/models.py b/src/agents/sandbox/app_server/models.py new file mode 100644 index 0000000000..70c61d44cd --- /dev/null +++ b/src/agents/sandbox/app_server/models.py @@ -0,0 +1,99 @@ +from __future__ import annotations + +from dataclasses import dataclass +from typing import TypeAlias + +from pydantic import BaseModel + +from .generated.v2_all import ( + AccountLoginCompletedNotification, + AccountRateLimitsUpdatedNotification, + AccountUpdatedNotification, + AgentMessageDeltaNotification, + AppListUpdatedNotification, + CommandExecutionOutputDeltaNotification, + ConfigWarningNotification, + ContextCompactedNotification, + DeprecationNoticeNotification, + ErrorNotification, + FileChangeOutputDeltaNotification, + ItemCompletedNotification, + ItemStartedNotification, + McpServerOauthLoginCompletedNotification, + McpToolCallProgressNotification, + PlanDeltaNotification, + RawResponseItemCompletedNotification, + ReasoningSummaryPartAddedNotification, + ReasoningSummaryTextDeltaNotification, + ReasoningTextDeltaNotification, + TerminalInteractionNotification, + ThreadNameUpdatedNotification, + ThreadStartedNotification, + ThreadTokenUsageUpdatedNotification, + TurnCompletedNotification, + TurnDiffUpdatedNotification, + TurnPlanUpdatedNotification, + TurnStartedNotification, + WindowsWorldWritableWarningNotification, +) + +JsonScalar: TypeAlias = str | int | float | bool | None +JsonValue: TypeAlias = JsonScalar | dict[str, "JsonValue"] | list["JsonValue"] +JsonObject: TypeAlias = dict[str, JsonValue] + + +@dataclass(slots=True) +class UnknownNotification: + params: JsonObject + + +NotificationPayload: TypeAlias = ( + AccountLoginCompletedNotification + | AccountRateLimitsUpdatedNotification + | AccountUpdatedNotification + | AgentMessageDeltaNotification + | AppListUpdatedNotification + | CommandExecutionOutputDeltaNotification + | ConfigWarningNotification + | ContextCompactedNotification + | DeprecationNoticeNotification + | ErrorNotification + | FileChangeOutputDeltaNotification + | ItemCompletedNotification + | ItemStartedNotification + | McpServerOauthLoginCompletedNotification + | McpToolCallProgressNotification + | PlanDeltaNotification + | RawResponseItemCompletedNotification + | ReasoningSummaryPartAddedNotification + | ReasoningSummaryTextDeltaNotification + | ReasoningTextDeltaNotification + | TerminalInteractionNotification + | ThreadNameUpdatedNotification + | ThreadStartedNotification + | ThreadTokenUsageUpdatedNotification + | TurnCompletedNotification + | TurnDiffUpdatedNotification + | TurnPlanUpdatedNotification + | TurnStartedNotification + | WindowsWorldWritableWarningNotification + | UnknownNotification +) + + +@dataclass(slots=True) +class Notification: + method: str + payload: NotificationPayload + + +class ServerInfo(BaseModel): + name: str | None = None + version: str | None = None + + +class InitializeResponse(BaseModel): + serverInfo: ServerInfo | None = None + userAgent: str | None = None + platformFamily: str | None = None + platformOs: str | None = None diff --git a/src/agents/sandbox/app_server/py.typed b/src/agents/sandbox/app_server/py.typed new file mode 100644 index 0000000000..e69de29bb2 diff --git a/src/agents/sandbox/app_server/retry.py b/src/agents/sandbox/app_server/retry.py new file mode 100644 index 0000000000..b7e4f77403 --- /dev/null +++ b/src/agents/sandbox/app_server/retry.py @@ -0,0 +1,41 @@ +from __future__ import annotations + +import random +import time +from typing import Callable, TypeVar + +from .errors import is_retryable_error + +T = TypeVar("T") + + +def retry_on_overload( + op: Callable[[], T], + *, + max_attempts: int = 3, + initial_delay_s: float = 0.25, + max_delay_s: float = 2.0, + jitter_ratio: float = 0.2, +) -> T: + """Retry helper for transient server-overload errors.""" + + if max_attempts < 1: + raise ValueError("max_attempts must be >= 1") + + delay = initial_delay_s + attempt = 0 + while True: + attempt += 1 + try: + return op() + except Exception as exc: + if attempt >= max_attempts: + raise + if not is_retryable_error(exc): + raise + + jitter = delay * jitter_ratio + sleep_for = min(max_delay_s, delay) + random.uniform(-jitter, jitter) + if sleep_for > 0: + time.sleep(sleep_for) + delay = min(max_delay_s, delay * 2) diff --git a/tests/test_sandbox_app_server_client.py b/tests/test_sandbox_app_server_client.py new file mode 100644 index 0000000000..5bbf59ef9e --- /dev/null +++ b/tests/test_sandbox_app_server_client.py @@ -0,0 +1,145 @@ +from __future__ import annotations + +import json +from collections import deque +from typing import Any, Callable + +import pytest + +from agents.sandbox.app_server.client import ( + AppServerClient, + AppServerConfig, + AppServerTransportOps, +) +from agents.sandbox.app_server.models import InitializeResponse, UnknownNotification + + +class _FakeConnection: + def __init__(self, responses: list[str | Callable[[list[dict[str, object]]], str]]) -> None: + self._responses = deque(responses) + self.sent_payloads: list[dict[str, object]] = [] + self.closed = False + + def send(self, message: str, text: bool | None = None) -> None: + assert text is True + self.sent_payloads.append(json.loads(message)) + + def recv(self, timeout: float | None = None) -> str: + assert timeout is None or timeout >= 0 + response = self._responses.popleft() + if callable(response): + return response(self.sent_payloads) + return response + + def close(self) -> None: + self.closed = True + + +def _initialize_result(sent_payloads: list[dict[str, object]]) -> str: + request = next(payload for payload in sent_payloads if payload.get("method") == "initialize") + return json.dumps( + { + "id": request["id"], + "result": { + "serverInfo": {"name": "codex-app-server", "version": "2"}, + "platformOs": "linux", + }, + } + ) + + +def test_app_server_client_initializes_over_explicit_websocket_url() -> None: + fake_connection = _FakeConnection([_initialize_result]) + captured_connect: dict[str, object] = {} + + def _connect(url: str, **kwargs: object) -> Any: + captured_connect["url"] = url + captured_connect["kwargs"] = kwargs + return fake_connection + + client = AppServerClient( + AppServerConfig( + websocket_url="ws://sandbox.example.test:4500/", + websocket_headers={"Authorization": "Bearer test"}, + ), + transport_ops=AppServerTransportOps(ws_connect=_connect), + ) + + try: + client.start() + response = client.initialize() + finally: + client.close() + + assert captured_connect["url"] == "ws://sandbox.example.test:4500/" + assert captured_connect["kwargs"] == { + "additional_headers": {"Authorization": "Bearer test"}, + "open_timeout": 10.0, + "max_size": None, + } + assert isinstance(response, InitializeResponse) + assert response.serverInfo is not None + assert response.serverInfo.name == "codex-app-server" + assert response.platformOs == "linux" + assert fake_connection.sent_payloads[0]["method"] == "initialize" + assert fake_connection.sent_payloads[1] == {"method": "initialized", "params": {}} + assert fake_connection.closed is True + + +def test_app_server_client_handles_server_requests_and_queues_notifications() -> None: + def _initialize_with_notification(sent_payloads: list[dict[str, object]]) -> str: + request = next( + payload for payload in sent_payloads if payload.get("method") == "initialize" + ) + return json.dumps({"id": request["id"], "result": {"platformOs": "linux"}}) + + fake_connection = _FakeConnection( + [ + json.dumps( + { + "id": "server-approval-1", + "method": "item/commandExecution/requestApproval", + "params": {"command": "ls"}, + } + ), + json.dumps({"method": "custom/notice", "params": {"seen": True}}), + _initialize_with_notification, + ] + ) + + def _connect(url: str, **kwargs: object) -> Any: + return fake_connection + + client = AppServerClient( + AppServerConfig(websocket_url="ws://sandbox.example.test:4500/"), + transport_ops=AppServerTransportOps(ws_connect=_connect), + ) + + try: + client.start() + response = client.initialize() + notification = client.next_notification() + finally: + client.close() + + assert response.platformOs == "linux" + assert fake_connection.sent_payloads[1] == { + "id": "server-approval-1", + "result": {"decision": "accept"}, + } + assert notification.method == "custom/notice" + assert isinstance(notification.payload, UnknownNotification) + assert notification.payload.params == {"seen": True} + + +def test_app_server_client_requires_websocket_url() -> None: + def _unused_connect(url: str, **kwargs: object) -> Any: + raise AssertionError("ws_connect should not be called without a websocket_url") + + client = AppServerClient( + AppServerConfig(), + transport_ops=AppServerTransportOps(ws_connect=_unused_connect), + ) + + with pytest.raises(ValueError, match="websocket_url is required"): + client.start() diff --git a/uv.lock b/uv.lock index b0d61b1c6b..7c587907e9 100644 --- a/uv.lock +++ b/uv.lock @@ -2073,6 +2073,7 @@ dependencies = [ { name = "requests" }, { name = "types-requests" }, { name = "typing-extensions" }, + { name = "websockets" }, ] [package.optional-dependencies] @@ -2170,6 +2171,7 @@ requires-dist = [ { name = "sqlalchemy", marker = "extra == 'sqlalchemy'", specifier = ">=2.0" }, { name = "types-requests", specifier = ">=2.0,<3" }, { name = "typing-extensions", specifier = ">=4.12.2,<5" }, + { name = "websockets", specifier = ">=15.0,<16" }, { name = "websockets", marker = "extra == 'realtime'", specifier = ">=15.0,<16" }, { name = "websockets", marker = "extra == 'voice'", specifier = ">=15.0,<16" }, ]

(RmDPN>a2NsMy@uN( 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