Skip to main content
The Daytona Runner module provides isolated sandbox execution for candidate agents in the Dream Arena phase. Each candidate runs in its own Daytona sandbox with full isolation, monitoring, and artifact capture.

Overview

The module handles:
  • Creating isolated sandboxes for each candidate
  • Uploading and executing candidate scripts
  • Capturing stdout and generated artifacts
  • Measuring execution time and success metrics
  • Automatic cleanup of sandbox resources
Requires the Daytona SDK (pip install daytona-sdk) and a valid DAYTONA_API_KEY in your environment.

Configuration

Environment Variables

DAYTONA_API_KEY
string
required
Your Daytona API key for authentication
DAYTONA_BASE_URL
string
default:"https://api.daytona.io/v1"
The Daytona API base URL

Configuration Functions

"""Get Daytona API key and base URL from environment.

Returns:
    tuple[Optional[str], Optional[str]]: (api_key, base_url)
"""

Core Functions

run_candidate_in_daytona

Run a single candidate script in a Daytona sandbox.
def run_candidate_in_daytona(
    candidate_id: str,
    script_path: str,
    objective: str,
    output_file: str,
    log_callback: Optional[Callable[[str, str], None]] = None,
) -> DaytonaResult:

Parameters

candidate_id
string
required
Identifier for the candidate (e.g., “alpha”, “beta”, “gamma”)
script_path
string
required
Local path to the candidate Python script to execute
objective
string
required
The objective string to pass as the first argument to the script
output_file
string
required
Path where the script should write its output artifact (inside sandbox)
log_callback
Callable[[str, str], None]
Optional callback for logging progress. Receives (candidate_id, message) for each log event.

Returns

DaytonaResult
DaytonaResult
Result object containing execution details and artifacts

Example

from src.daytona_runner import run_candidate_in_daytona

result = run_candidate_in_daytona(
    candidate_id="alpha",
    script_path="candidates/agent_alpha.py",
    objective="Generate AI events for Discord",
    output_file="/home/daytona/output.md",
)

if result.success:
    print(f"Execution time: {result.runtime_seconds:.2f}s")
    print(f"Artifact content:\n{result.artifact_content}")
else:
    print(f"Error: {result.error_message}")

Execution Flow

  1. Initialize - Connect to Daytona API with credentials
  2. Create Sandbox - Provision isolated sandbox environment
  3. Upload Script - Base64 encode and transfer script to sandbox
  4. Execute - Run script with objective and output path arguments
  5. Capture Output - Collect stdout and read artifact file
  6. Cleanup - Delete sandbox resources
  7. Return Result - Package all data into DaytonaResult

run_all_candidates_in_daytona

Run multiple candidates in parallel, each in their own sandbox.
def run_all_candidates_in_daytona(
    candidates: list[dict],
    objective: str,
    artifacts_dir: Path,
    log_callback: Optional[Callable[[str, str], None]] = None,
) -> list[DaytonaResult]:

Parameters

candidates
list[dict]
required
List of candidate dictionaries with id and script keys
[
    {"id": "alpha", "script": "candidates/agent_alpha.py"},
    {"id": "beta", "script": "candidates/agent_beta.py"},
    {"id": "gamma", "script": "candidates/agent_gamma.py"},
]
objective
string
required
The objective to pass to all candidates
artifacts_dir
Path
required
Local directory where artifacts will be saved (creates subdirectories per candidate)
log_callback
Callable[[str, str], None]
Optional callback for logging progress from all candidates

Returns

results
list[DaytonaResult]
List of result objects, one per candidate, in the same order as input

Example

from pathlib import Path
from src.daytona_runner import run_all_candidates_in_daytona

candidates = [
    {"id": "alpha", "script": "candidates/agent_alpha.py"},
    {"id": "beta", "script": "candidates/agent_beta.py"},
    {"id": "gamma", "script": "candidates/agent_gamma.py"},
]

results = run_all_candidates_in_daytona(
    candidates=candidates,
    objective="Build a Discord bot for AI events",
    artifacts_dir=Path("artifacts/round_1"),
)

# Analyze results
for result in results:
    print(f"{result.candidate_id}: {result.success} ({result.runtime_seconds:.1f}s)")
    if result.artifact_content:
        print(f"  Artifact: {len(result.artifact_content)} chars")

Data Classes

DaytonaResult

Contains the complete execution result for a candidate.
@dataclass
class DaytonaResult:
    """Result from running a candidate in Daytona."""
    candidate_id: str
    sandbox_id: Optional[str]
    success: bool
    output: str
    artifact_content: Optional[str]
    runtime_seconds: float
    error_message: Optional[str] = None

Fields

candidate_id
string
The candidate identifier (“alpha”, “beta”, “gamma”)
sandbox_id
string | null
The Daytona sandbox ID (first 8 characters shown in logs)
success
boolean
Whether the execution completed successfully (exit code 0 and artifact retrieved)
output
string
Standard output from the script execution
artifact_content
string | null
The contents of the output file, if successfully retrieved
runtime_seconds
float
Total execution time including sandbox creation and cleanup
error_message
string | null
Error description if success is False

Sandbox Configuration

Each sandbox runs with the following configuration:
# Default working directory
/home/daytona/

# Python version
python3 (system default)

# Script location pattern
/home/daytona/{candidate_id}.py

# Output location pattern
/home/daytona/{candidate_id}_output.md

Error Handling

The module handles several error scenarios gracefully:
# Returns DaytonaResult with success=False
if not DAYTONA_API_KEY:
    error_message = "Daytona API key not configured"

if not DAYTONA_AVAILABLE:
    error_message = "Daytona SDK not installed"

Best Practices

Sandbox Lifecycle: Sandboxes are automatically created and destroyed for each execution. No manual cleanup is required.
Timeouts: Scripts have 60 seconds to complete. Ensure your candidate scripts finish within this window.
Artifact Retrieval: The script must write to the exact output_file path provided. Use sys.argv[2] to get this path.
Shell Escaping: Objectives containing quotes are automatically escaped. Pass them directly without manual escaping.

Build docs developers (and LLMs) love