87 lines
4.3 KiB
Markdown
87 lines
4.3 KiB
Markdown
# Temporal Sandbox Agent
|
|
|
|
A conversational coding agent that runs as a durable Temporal workflow with support for multiple sandbox backends (Daytona, Docker, E2B, local unix).
|
|
|
|
## Quickstart
|
|
|
|
**Prerequisites:** Docker (for the Docker backend) and API keys for any cloud backends you want to use. The local and Docker sandboxes work without any cloud provider API keys.
|
|
|
|
## Local smoke test
|
|
|
|
If you only want to confirm that Temporal workflows run locally, use the minimal
|
|
example first:
|
|
|
|
```
|
|
export OPENAI_API_KEY="sk-..."
|
|
# Optional: export EXAMPLES_TEMPORAL_MODEL="gpt-5.4-mini"
|
|
# Optional: export EXAMPLES_TEMPORAL_TRACE="openai"
|
|
uv run --extra temporal python -m examples.sandbox.extensions.temporal.local_hello_workflow
|
|
```
|
|
|
|
This starts the Temporal Python SDK test server, runs one workflow and one model activity, connects the workflow to a local Unix sandbox, and then shuts down. It does not require the Temporal CLI, an already running Temporal dev server, or sandbox backend credentials.
|
|
|
|
The local smoke test enables OpenAI Agents tracing by default. Set `EXAMPLES_TEMPORAL_TRACE=none` to disable tracing, or `EXAMPLES_TEMPORAL_TRACE=openai_with_temporal_spans` to also ask the Temporal plugin to add Temporal spans. The Temporal span mode depends on Temporal plugin behavior and may omit regular Agents spans with some plugin versions; use the default `openai` mode when you want standard OpenAI trace spans.
|
|
|
|
1. Install [just](https://just.systems/man/en/packages.html) and the [Temporal CLI](https://docs.temporal.io/cli/setup-cli#install-the-cli) if you don't have them already.
|
|
|
|
2. Change into the example directory:
|
|
|
|
```
|
|
cd examples/sandbox/extensions/temporal
|
|
```
|
|
|
|
3. Create a `.env` file in this directory with your API keys:
|
|
|
|
```
|
|
OPENAI_API_KEY="sk-..."
|
|
DAYTONA_API_KEY="dtn_..." # optional, for Daytona backend
|
|
E2B_API_KEY="e2b_..." # optional, for E2B backend
|
|
```
|
|
|
|
4. Start the Temporal dev server:
|
|
|
|
```
|
|
just temporal
|
|
```
|
|
|
|
5. In a second terminal, start the worker:
|
|
|
|
```
|
|
just worker
|
|
```
|
|
|
|
6. In a third terminal, start the TUI:
|
|
|
|
```
|
|
just tui
|
|
```
|
|
|
|
The `just worker` and `just tui` commands automatically install dependencies before starting.
|
|
|
|
## TUI commands
|
|
|
|
| Command | Description |
|
|
|--------------------|--------------------------------------------------------|
|
|
| `/switch` | Switch the current session to a different sandbox backend |
|
|
| `/fork [title]` | Fork the session onto a (possibly different) backend |
|
|
| `/title <name>` | Rename the current session |
|
|
| `/done` | Exit the TUI |
|
|
|
|
Both `/switch` and `/fork` open an interactive backend picker. When switching to the local backend you can specify the workspace root directory.
|
|
|
|
## How it works
|
|
|
|
A single Temporal worker registers all sandbox backends via `SandboxClientProvider`, so every backend's activities are available on one task queue. The workflow picks which backend to target each turn by calling `temporal_sandbox_client(name)` in its `RunConfig`.
|
|
|
|
**Files:**
|
|
|
|
- `temporal_sandbox_agent.py` -- The `AgentWorkflow` definition and worker entrypoint. Each conversation turn calls `Runner.run()` with a `SandboxRunConfig` that targets the active backend. The workflow is
|
|
long-lived: it idles between turns and persists indefinitely in Temporal.
|
|
- `temporal_session_manager.py` -- A singleton `SessionManagerWorkflow` that tracks active sessions and handles create, fork, switch, and destroy operations.
|
|
- `temporal_sandbox_tui.py` -- A [Textual](https://textual.textualize.io/) TUI that connects to the session manager and drives conversations via signals, updates, and queries.
|
|
- `examples/sandbox/misc/workspace_shell.py` -- A shared `Capability` that gives the agent a shell tool for running commands in the sandbox workspace.
|
|
|
|
**Switching backends** is an in-place operation: the workflow receives a `switch_backend` update, changes its backend and manifest, clears the backend-specific session state, and the next turn creates a fresh session on the new backend. The portable snapshot is preserved so workspace files carry over.
|
|
|
|
**Forking** pauses the source workflow, snapshots its state and conversation history, and starts a new child workflow on the chosen backend. The fork gets an independent copy of the workspace and conversation.
|