From efdfe28f94843d90eceeab8ae26354ee62af2002 Mon Sep 17 00:00:00 2001 From: Brian Strauch Date: Tue, 18 Aug 2026 12:49:10 -0700 Subject: [PATCH 1/3] Add a sandbox sample and snipsync markers for the docs guide MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The Python OpenAI Agents SDK integration guide on docs.temporal.io currently sends readers to the SDK contrib README. Give it code to pull from here instead. Adds openai_agents/sandbox, covering SandboxAgent through the plugin. It uses UnixLocalSandboxClient, so it runs with only OPENAI_API_KEY and no sandbox account — at the cost of giving the agent a shell on the worker host, which the README calls out. Note the class lives in agents.sandbox.sandboxes.unix_local, not the agents.extensions.sandbox.unix_local path the contrib README shows. Adds snipsync markers to the samples that guide walks through, scoped to exactly the code it shows so no block needs selectedLines. Markers around indented code sit at that code's indent level, keeping snipsync's dedent working (see #346). Co-Authored-By: Claude Opus 5 (1M context) --- openai_agents/README.md | 1 + .../workflows/agents_as_tools_workflow.py | 4 ++ .../basic/activities/get_weather_activity.py | 4 ++ .../basic/run_hello_world_workflow.py | 2 + openai_agents/basic/run_worker.py | 2 + .../basic/workflows/hello_world_workflow.py | 4 ++ .../basic/workflows/tools_workflow.py | 4 ++ .../workflows/customer_service_workflow.py | 3 + .../workflows/approval_mcp_workflow.py | 4 ++ .../workflows/simple_mcp_workflow.py | 4 ++ openai_agents/mcp/run_file_system_worker.py | 2 + .../run_memory_research_scratchpad_worker.py | 2 + .../mcp/workflows/file_system_workflow.py | 2 + .../memory_research_scratchpad_workflow.py | 2 + openai_agents/sandbox/README.md | 59 +++++++++++++++++ .../sandbox/run_local_sandbox_workflow.py | 31 +++++++++ openai_agents/sandbox/run_worker.py | 51 +++++++++++++++ openai_agents/sandbox/shared.py | 9 +++ .../workflows/local_sandbox_workflow.py | 65 +++++++++++++++++++ .../streaming/run_stream_text_workflow.py | 2 + .../workflows/stream_text_workflow.py | 4 ++ .../tools/workflows/web_search_workflow.py | 4 ++ 22 files changed, 265 insertions(+) create mode 100644 openai_agents/sandbox/README.md create mode 100644 openai_agents/sandbox/run_local_sandbox_workflow.py create mode 100644 openai_agents/sandbox/run_worker.py create mode 100644 openai_agents/sandbox/shared.py create mode 100644 openai_agents/sandbox/workflows/local_sandbox_workflow.py diff --git a/openai_agents/README.md b/openai_agents/README.md index f6857d795..01c9377fd 100644 --- a/openai_agents/README.md +++ b/openai_agents/README.md @@ -39,4 +39,5 @@ Each directory contains a complete example with its own README for detailed inst - **[Customer Service](./customer_service/README.md)** - Interactive customer service agent with escalation capabilities, demonstrating conversational workflows. - **[Reasoning Content](./reasoning_content/README.md)** - Example of how to retrieve the thought process of reasoning models. - **[Financial Research Agent](./financial_research_agent/README.md)** - Multi-agent financial research system with planner, search, analyst, writer, and verifier agents collaborating. +- **[Sandbox](./sandbox/README.md)** - `SandboxAgent` with a shell and filesystem, where every sandbox operation runs as a Temporal activity. **Pre-release.** - **[Streaming](./streaming/README.md)** - `Runner.run_streamed` with buffered token streaming to external subscribers via `temporalio.contrib.workflow_streams`. **Experimental.** diff --git a/openai_agents/agent_patterns/workflows/agents_as_tools_workflow.py b/openai_agents/agent_patterns/workflows/agents_as_tools_workflow.py index db849c1cd..126c09fa8 100644 --- a/openai_agents/agent_patterns/workflows/agents_as_tools_workflow.py +++ b/openai_agents/agent_patterns/workflows/agents_as_tools_workflow.py @@ -8,6 +8,7 @@ """ +# @@@SNIPSTART python-openai-agents-agent-as-tool-workflow def orchestrator_agent() -> Agent: spanish_agent = Agent( name="spanish_agent", @@ -52,6 +53,9 @@ def orchestrator_agent() -> Agent: return orchestrator_agent +# @@@SNIPEND + + def synthesizer_agent() -> Agent: return Agent( name="synthesizer_agent", diff --git a/openai_agents/basic/activities/get_weather_activity.py b/openai_agents/basic/activities/get_weather_activity.py index c8be473c4..8afcf0a44 100644 --- a/openai_agents/basic/activities/get_weather_activity.py +++ b/openai_agents/basic/activities/get_weather_activity.py @@ -1,3 +1,4 @@ +# @@@SNIPSTART python-openai-agents-weather-activity from dataclasses import dataclass from temporalio import activity @@ -16,3 +17,6 @@ async def get_weather(city: str) -> Weather: Get the weather for a given city. """ return Weather(city=city, temperature_range="14-20C", conditions="Sunny with wind.") + + +# @@@SNIPEND diff --git a/openai_agents/basic/run_hello_world_workflow.py b/openai_agents/basic/run_hello_world_workflow.py index 0662a4fa3..9893d4052 100644 --- a/openai_agents/basic/run_hello_world_workflow.py +++ b/openai_agents/basic/run_hello_world_workflow.py @@ -8,6 +8,7 @@ async def main(): # Create client connected to server at the given address + # @@@SNIPSTART python-openai-agents-hello-world-client client = await Client.connect( "localhost:7233", plugins=[ @@ -23,6 +24,7 @@ async def main(): task_queue="openai-agents-basic-task-queue", ) print(f"Result: {result}") + # @@@SNIPEND if __name__ == "__main__": diff --git a/openai_agents/basic/run_worker.py b/openai_agents/basic/run_worker.py index 94d6a8823..3586c8a2f 100644 --- a/openai_agents/basic/run_worker.py +++ b/openai_agents/basic/run_worker.py @@ -34,6 +34,7 @@ async def main(): # Create client connected to server at the given address + # @@@SNIPSTART python-openai-agents-hello-world-worker client = await Client.connect( "localhost:7233", plugins=[ @@ -44,6 +45,7 @@ async def main(): ), ], ) + # @@@SNIPEND worker = Worker( client, diff --git a/openai_agents/basic/workflows/hello_world_workflow.py b/openai_agents/basic/workflows/hello_world_workflow.py index dd6b2e41b..a62eb32cc 100644 --- a/openai_agents/basic/workflows/hello_world_workflow.py +++ b/openai_agents/basic/workflows/hello_world_workflow.py @@ -1,3 +1,4 @@ +# @@@SNIPSTART python-openai-agents-hello-world-workflow from agents import Agent, Runner from temporalio import workflow @@ -13,3 +14,6 @@ async def run(self, prompt: str) -> str: result = await Runner.run(agent, input=prompt) return result.final_output + + +# @@@SNIPEND diff --git a/openai_agents/basic/workflows/tools_workflow.py b/openai_agents/basic/workflows/tools_workflow.py index 70964dc09..d9c79d596 100644 --- a/openai_agents/basic/workflows/tools_workflow.py +++ b/openai_agents/basic/workflows/tools_workflow.py @@ -9,6 +9,7 @@ from openai_agents.basic.activities.get_weather_activity import get_weather +# @@@SNIPSTART python-openai-agents-activity-tool-workflow @workflow.defn class ToolsWorkflow: @workflow.run @@ -25,3 +26,6 @@ async def run(self, question: str) -> str: result = await Runner.run(agent, input=question) return result.final_output + + +# @@@SNIPEND diff --git a/openai_agents/customer_service/workflows/customer_service_workflow.py b/openai_agents/customer_service/workflows/customer_service_workflow.py index 0157d0508..49a0aa423 100644 --- a/openai_agents/customer_service/workflows/customer_service_workflow.py +++ b/openai_agents/customer_service/workflows/customer_service_workflow.py @@ -56,6 +56,7 @@ def __init__( customer_service_state.input_items if customer_service_state else [] ) + # @@@SNIPSTART python-openai-agents-continue-as-new-workflow @workflow.run async def run( self, customer_service_state: CustomerServiceWorkflowState | None = None @@ -73,6 +74,8 @@ async def run( ) ) + # @@@SNIPEND + @workflow.query def get_chat_history(self) -> list[str]: return self.printed_history diff --git a/openai_agents/hosted_mcp/workflows/approval_mcp_workflow.py b/openai_agents/hosted_mcp/workflows/approval_mcp_workflow.py index 1b5b7b6f9..9f85343a3 100644 --- a/openai_agents/hosted_mcp/workflows/approval_mcp_workflow.py +++ b/openai_agents/hosted_mcp/workflows/approval_mcp_workflow.py @@ -10,6 +10,7 @@ from temporalio import workflow +# @@@SNIPSTART python-openai-agents-hosted-mcp-approval-workflow def approval_callback(request: MCPToolApprovalRequest) -> MCPToolApprovalFunctionResult: """Simple approval callback that logs the request and approves by default. @@ -23,6 +24,9 @@ def approval_callback(request: MCPToolApprovalRequest) -> MCPToolApprovalFunctio return result +# @@@SNIPEND + + @workflow.defn class ApprovalMCPWorkflow: @workflow.run diff --git a/openai_agents/hosted_mcp/workflows/simple_mcp_workflow.py b/openai_agents/hosted_mcp/workflows/simple_mcp_workflow.py index 2fac64bc5..ab12c1d14 100644 --- a/openai_agents/hosted_mcp/workflows/simple_mcp_workflow.py +++ b/openai_agents/hosted_mcp/workflows/simple_mcp_workflow.py @@ -4,6 +4,7 @@ from temporalio import workflow +# @@@SNIPSTART python-openai-agents-hosted-mcp-workflow @workflow.defn class SimpleMCPWorkflow: @workflow.run @@ -26,3 +27,6 @@ async def run( result = await Runner.run(agent, question) return result.final_output + + +# @@@SNIPEND diff --git a/openai_agents/mcp/run_file_system_worker.py b/openai_agents/mcp/run_file_system_worker.py index 2ed8dffdf..0deb8463e 100644 --- a/openai_agents/mcp/run_file_system_worker.py +++ b/openai_agents/mcp/run_file_system_worker.py @@ -23,6 +23,7 @@ async def main(): current_dir = os.path.dirname(os.path.abspath(__file__)) samples_dir = os.path.join(current_dir, "sample_files") + # @@@SNIPSTART python-openai-agents-stateless-mcp-worker file_system_server = StatelessMCPServerProvider( "FileSystemServer", lambda: MCPServerStdio( @@ -48,6 +49,7 @@ async def main(): ), ], ) + # @@@SNIPEND worker = Worker( client, diff --git a/openai_agents/mcp/run_memory_research_scratchpad_worker.py b/openai_agents/mcp/run_memory_research_scratchpad_worker.py index 536ab9745..ae590ad38 100644 --- a/openai_agents/mcp/run_memory_research_scratchpad_worker.py +++ b/openai_agents/mcp/run_memory_research_scratchpad_worker.py @@ -22,6 +22,7 @@ async def main(): logging.basicConfig(level=logging.INFO) + # @@@SNIPSTART python-openai-agents-stateful-mcp-worker memory_server_provider = StatefulMCPServerProvider( "MemoryServer", lambda _: MCPServerStdio( @@ -47,6 +48,7 @@ async def main(): ), ], ) + # @@@SNIPEND worker = Worker( client, diff --git a/openai_agents/mcp/workflows/file_system_workflow.py b/openai_agents/mcp/workflows/file_system_workflow.py index b3528c185..7ee6ab885 100644 --- a/openai_agents/mcp/workflows/file_system_workflow.py +++ b/openai_agents/mcp/workflows/file_system_workflow.py @@ -11,6 +11,7 @@ class FileSystemWorkflow: @workflow.run async def run(self) -> str: with trace(workflow_name="MCP File System Example"): + # @@@SNIPSTART python-openai-agents-stateless-mcp-workflow server: MCPServer = openai_agents.workflow.stateless_mcp_server( "FileSystemServer" ) @@ -19,6 +20,7 @@ async def run(self) -> str: instructions="Use the tools to read the filesystem and answer questions based on those files.", mcp_servers=[server], ) + # @@@SNIPEND # List the files it can read message = "Read the files and list them." diff --git a/openai_agents/mcp/workflows/memory_research_scratchpad_workflow.py b/openai_agents/mcp/workflows/memory_research_scratchpad_workflow.py index 1812a4521..a381843a9 100644 --- a/openai_agents/mcp/workflows/memory_research_scratchpad_workflow.py +++ b/openai_agents/mcp/workflows/memory_research_scratchpad_workflow.py @@ -43,6 +43,7 @@ class MemoryResearchScratchpadWorkflow: @workflow.run async def run(self) -> str: + # @@@SNIPSTART python-openai-agents-stateful-mcp-workflow async with temporal_openai_agents.workflow.stateful_mcp_server( "MemoryServer", ) as server: @@ -57,6 +58,7 @@ async def run(self) -> str: mcp_servers=[server], model_settings=ModelSettings(tool_choice="required"), ) + # @@@SNIPEND # Step 1: Write seed notes to memory write_prompt_lines = [ diff --git a/openai_agents/sandbox/README.md b/openai_agents/sandbox/README.md new file mode 100644 index 000000000..61ef96734 --- /dev/null +++ b/openai_agents/sandbox/README.md @@ -0,0 +1,59 @@ +# Sandbox OpenAI Agents + +> **Pre-release.** Sandbox support in `temporalio.contrib.openai_agents` is +> subject to change before general availability. + +Before running this example, be sure to review the +[prerequisites and background on the integration](../README.md). + +`SandboxAgent` from the OpenAI Agents SDK gives an agent a machine to work on: +a shell it can run commands in and a filesystem it can read and write. The +plugin runs every one of those operations as a Temporal activity against a +`SandboxClientProvider` registered on the worker, so sandbox work is +observable, retryable, and recoverable like any other activity. The sandbox +session state is serialized with the workflow, so a worker restart part-way +through a run resumes against the same session. + +The workflow refers to a backend by name. `temporal_sandbox_client("local")` +resolves to whichever `SandboxClientProvider` the worker registered under +`"local"`, and the name becomes the prefix of that backend's activity names — +which is what lets several backends coexist on one worker. Names must match +exactly. + +This sample uses `UnixLocalSandboxClient`, which runs commands on the worker +host and needs no credentials beyond `OPENAI_API_KEY`. **The agent gets a real +shell on the machine running the worker**, so treat it accordingly: for +anything you would not run locally, register a remote client such as +`DaytonaSandboxClient` or `E2BSandboxClient` from +`agents.extensions.sandbox` instead. Only the worker changes — the workflow +still just names a provider. + +## Running the Example + +First, start the worker: + +```bash +uv run openai_agents/sandbox/run_worker.py +``` + +Then, in another terminal, run the workflow: + +```bash +uv run openai_agents/sandbox/run_local_sandbox_workflow.py +``` + +The agent writes a file in the sandbox, reads it back, and reports what it +found. In the Web UI at http://localhost:8233 the run shows the model +activities interleaved with the `local-sandbox_session_*` activities that carry +out the sandbox work. + +## Notes + +* A default `SandboxAgent` already carries the `Filesystem`, `Shell`, and + `Compaction` capabilities, so this sample declares no tools of its own. +* `temporal_sandbox_client()` takes an optional `ActivityConfig` for timeouts + and retries on the sandbox activities. It defaults to a 5-minute + `start_to_close_timeout`. +* A single workflow can target several backends by calling + `temporal_sandbox_client()` once per name, as long as the worker registers a + provider for each. diff --git a/openai_agents/sandbox/run_local_sandbox_workflow.py b/openai_agents/sandbox/run_local_sandbox_workflow.py new file mode 100644 index 000000000..f3b285458 --- /dev/null +++ b/openai_agents/sandbox/run_local_sandbox_workflow.py @@ -0,0 +1,31 @@ +from __future__ import annotations + +import asyncio + +from temporalio.client import Client +from temporalio.contrib.openai_agents import OpenAIAgentsPlugin + +from openai_agents.sandbox.shared import TASK_QUEUE +from openai_agents.sandbox.workflows.local_sandbox_workflow import ( + LocalSandboxWorkflow, +) + + +async def main() -> None: + client = await Client.connect( + "localhost:7233", + plugins=[OpenAIAgentsPlugin()], + ) + + result = await client.execute_workflow( + LocalSandboxWorkflow.run, + "Write a file holding the first 20 Fibonacci numbers, one per line, " + "then tell me how many lines it has and what the last one is.", + id="openai-agents-sandbox", + task_queue=TASK_QUEUE, + ) + print(f"Result: {result}") + + +if __name__ == "__main__": + asyncio.run(main()) diff --git a/openai_agents/sandbox/run_worker.py b/openai_agents/sandbox/run_worker.py new file mode 100644 index 000000000..c970f5920 --- /dev/null +++ b/openai_agents/sandbox/run_worker.py @@ -0,0 +1,51 @@ +from __future__ import annotations + +import asyncio +from datetime import timedelta + +from agents.sandbox.sandboxes.unix_local import UnixLocalSandboxClient +from temporalio.client import Client +from temporalio.contrib.openai_agents import ( + ModelActivityParameters, + OpenAIAgentsPlugin, + SandboxClientProvider, +) +from temporalio.worker import Worker + +from openai_agents.sandbox.shared import SANDBOX_PROVIDER, TASK_QUEUE +from openai_agents.sandbox.workflows.local_sandbox_workflow import ( + LocalSandboxWorkflow, +) + + +async def main() -> None: + # @@@SNIPSTART python-openai-agents-sandbox-worker + client = await Client.connect( + "localhost:7233", + plugins=[ + OpenAIAgentsPlugin( + model_params=ModelActivityParameters( + start_to_close_timeout=timedelta(seconds=60) + ), + # The plugin registers one set of sandbox activities per + # provider, prefixed with the provider name. Register several + # providers to let one worker serve several backends. + sandbox_clients=[ + SandboxClientProvider(SANDBOX_PROVIDER, UnixLocalSandboxClient()), + ], + ), + ], + ) + # @@@SNIPEND + + worker = Worker( + client, + task_queue=TASK_QUEUE, + workflows=[LocalSandboxWorkflow], + ) + print("Worker started. Ctrl+C to exit.") + await worker.run() + + +if __name__ == "__main__": + asyncio.run(main()) diff --git a/openai_agents/sandbox/shared.py b/openai_agents/sandbox/shared.py new file mode 100644 index 000000000..377bf972f --- /dev/null +++ b/openai_agents/sandbox/shared.py @@ -0,0 +1,9 @@ +from __future__ import annotations + +TASK_QUEUE = "openai-agents-sandbox-task-queue" + +# Name the worker registers its SandboxClientProvider under, and the name the +# workflow passes to temporal_sandbox_client(). The two must match exactly: +# the name becomes the prefix of that backend's activity names, which is what +# lets several backends share one worker. +SANDBOX_PROVIDER = "local" diff --git a/openai_agents/sandbox/workflows/local_sandbox_workflow.py b/openai_agents/sandbox/workflows/local_sandbox_workflow.py new file mode 100644 index 000000000..54cd12086 --- /dev/null +++ b/openai_agents/sandbox/workflows/local_sandbox_workflow.py @@ -0,0 +1,65 @@ +"""A ``SandboxAgent`` whose sandbox operations run as Temporal activities. + +``SandboxAgent`` gives an agent a real machine to work on: it can run shell +commands and read and write files. The plugin routes every one of those +operations — creating the session, each ``exec``, each read and write, and the +teardown — through a Temporal activity against the ``SandboxClientProvider`` +registered on the worker under the name passed to +``temporal_sandbox_client()``. + +Two consequences worth knowing: + +1. Each sandbox operation is individually retryable and shows up in workflow + history, so a flaky command is a retried activity rather than a lost run. +2. The sandbox session state is serialized with the workflow, so a worker + restart mid-run resumes against the same session instead of starting over. + +This sample uses the local Unix backend, which runs commands on the worker +host and needs no credentials. Swap in a remote client such as +``DaytonaSandboxClient`` for anything you would not run on your own machine — +only the worker changes, the workflow just names a different provider. +""" + +from __future__ import annotations + +from agents import Runner +from agents.run import RunConfig +from agents.sandbox import SandboxAgent, SandboxRunConfig +from agents.sandbox.sandboxes.unix_local import UnixLocalSandboxClientOptions +from temporalio import workflow +from temporalio.contrib.openai_agents.workflow import temporal_sandbox_client + +from openai_agents.sandbox.shared import SANDBOX_PROVIDER + + +# @@@SNIPSTART python-openai-agents-sandbox-workflow +@workflow.defn +class LocalSandboxWorkflow: + @workflow.run + async def run(self, prompt: str) -> str: + # A default SandboxAgent already carries the Filesystem, Shell, and + # Compaction capabilities, so there are no tools to declare here. + agent = SandboxAgent[None]( + name="Sandbox Assistant", + instructions=( + "You have a sandbox with a shell and a filesystem. Use it to do " + "the work rather than answering from memory, then report what " + "the commands returned." + ), + ) + + result = await Runner.run( + starting_agent=agent, + input=prompt, + run_config=RunConfig( + sandbox=SandboxRunConfig( + # Must match the name registered on the worker. + client=temporal_sandbox_client(SANDBOX_PROVIDER), + options=UnixLocalSandboxClientOptions(), + ), + ), + ) + return result.final_output_as(str, raise_if_incorrect_type=True) + + +# @@@SNIPEND diff --git a/openai_agents/streaming/run_stream_text_workflow.py b/openai_agents/streaming/run_stream_text_workflow.py index 5b3181a2d..5f51ebeb2 100644 --- a/openai_agents/streaming/run_stream_text_workflow.py +++ b/openai_agents/streaming/run_stream_text_workflow.py @@ -51,6 +51,7 @@ async def main() -> None: task_queue=TASK_QUEUE, ) + # @@@SNIPSTART python-openai-agents-streaming-client stream = WorkflowStreamClient.create(client, workflow_id) converter = client.data_converter.payload_converter @@ -88,6 +89,7 @@ async def main() -> None: if isinstance(event, ResponseTextDeltaEvent): print(event.delta, end="", flush=True) + # @@@SNIPEND result = await handle.result() print("\n--- final result ---") diff --git a/openai_agents/streaming/workflows/stream_text_workflow.py b/openai_agents/streaming/workflows/stream_text_workflow.py index abbd8b23c..54a31228e 100644 --- a/openai_agents/streaming/workflows/stream_text_workflow.py +++ b/openai_agents/streaming/workflows/stream_text_workflow.py @@ -41,6 +41,7 @@ class StreamTextInput: stream_state: WorkflowStreamState | None = None +# @@@SNIPSTART python-openai-agents-streaming-workflow @workflow.defn class StreamTextWorkflow: @workflow.init @@ -81,3 +82,6 @@ async def run(self, input: StreamTextInput) -> str: # message output, so assert the str this signature promises rather # than letting a None through. return result.final_output_as(str, raise_if_incorrect_type=True) + + +# @@@SNIPEND diff --git a/openai_agents/tools/workflows/web_search_workflow.py b/openai_agents/tools/workflows/web_search_workflow.py index 8b505ac14..208396301 100644 --- a/openai_agents/tools/workflows/web_search_workflow.py +++ b/openai_agents/tools/workflows/web_search_workflow.py @@ -4,6 +4,7 @@ from temporalio import workflow +# @@@SNIPSTART python-openai-agents-hosted-tool-workflow @workflow.defn class WebSearchWorkflow: @workflow.run @@ -18,3 +19,6 @@ async def run(self, question: str, user_city: str = "New York") -> str: result = await Runner.run(agent, question) return result.final_output + + +# @@@SNIPEND From 07db957ad89ae4ce61608ddee071ade4e20b4e58 Mon Sep 17 00:00:00 2001 From: Brian Strauch Date: Tue, 18 Aug 2026 14:58:47 -0700 Subject: [PATCH 2/3] Apply suggestion from @brianstrauch --- openai_agents/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openai_agents/README.md b/openai_agents/README.md index 01c9377fd..eaf86bb88 100644 --- a/openai_agents/README.md +++ b/openai_agents/README.md @@ -39,5 +39,5 @@ Each directory contains a complete example with its own README for detailed inst - **[Customer Service](./customer_service/README.md)** - Interactive customer service agent with escalation capabilities, demonstrating conversational workflows. - **[Reasoning Content](./reasoning_content/README.md)** - Example of how to retrieve the thought process of reasoning models. - **[Financial Research Agent](./financial_research_agent/README.md)** - Multi-agent financial research system with planner, search, analyst, writer, and verifier agents collaborating. -- **[Sandbox](./sandbox/README.md)** - `SandboxAgent` with a shell and filesystem, where every sandbox operation runs as a Temporal activity. **Pre-release.** +- **[Sandbox](./sandbox/README.md)** - `SandboxAgent` with a shell and filesystem, where every sandbox operation runs as a Temporal activity. **Experimental.** - **[Streaming](./streaming/README.md)** - `Runner.run_streamed` with buffered token streaming to external subscribers via `temporalio.contrib.workflow_streams`. **Experimental.** From 0ca0a84b7167dd8f397d05b831902e92cf4a1801 Mon Sep 17 00:00:00 2001 From: Brian Strauch Date: Tue, 18 Aug 2026 15:03:16 -0700 Subject: [PATCH 3/3] Apply suggestion from @brianstrauch --- openai_agents/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openai_agents/README.md b/openai_agents/README.md index eaf86bb88..01c9377fd 100644 --- a/openai_agents/README.md +++ b/openai_agents/README.md @@ -39,5 +39,5 @@ Each directory contains a complete example with its own README for detailed inst - **[Customer Service](./customer_service/README.md)** - Interactive customer service agent with escalation capabilities, demonstrating conversational workflows. - **[Reasoning Content](./reasoning_content/README.md)** - Example of how to retrieve the thought process of reasoning models. - **[Financial Research Agent](./financial_research_agent/README.md)** - Multi-agent financial research system with planner, search, analyst, writer, and verifier agents collaborating. -- **[Sandbox](./sandbox/README.md)** - `SandboxAgent` with a shell and filesystem, where every sandbox operation runs as a Temporal activity. **Experimental.** +- **[Sandbox](./sandbox/README.md)** - `SandboxAgent` with a shell and filesystem, where every sandbox operation runs as a Temporal activity. **Pre-release.** - **[Streaming](./streaming/README.md)** - `Runner.run_streamed` with buffered token streaming to external subscribers via `temporalio.contrib.workflow_streams`. **Experimental.**