Skip to main content
In this guide you’ll run a coding agent that can create files, execute shell commands, and search a codebase - all with built-in sandbox restrictions and human approval for every destructive action.

Prerequisites

Install Polos

Run the install script to download the Polos server and CLI:
curl -fsSL https://install.polos.dev/install.sh | bash
Add the Polos bin directory to your PATH (if not already added):
export PATH="$HOME/.polos/bin:$PATH"
Add this line to your shell profile (~/.bashrc, ~/.zshrc, etc.) to make it permanent.

Start the Polos server

Start the orchestrator and UI:
polos-server start
You should see output like:
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
📡 Orchestrator API: http://127.0.0.1:8080
🌐 UI:               http://127.0.0.1:5173
🔑 Project ID:       046a270d-c253-4914-9d2c-86b8484807ae
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
Copy the Project ID - you’ll need it in the next step.
By default, Polos uses port 8080 for the orchestrator and 5173 for the UI. To use different ports:
export POLOS_ORCHESTRATOR_PORT=9000
export POLOS_UI_PORT=3000
polos-server start
If Polos has already been initialized, update ~/.polos/config.toml with the new ports before starting.

Clone the example

Clone the local sandbox agent example:
git clone https://github.com/polos-dev/polos.git
cd polos/python-examples/21-local-sandbox
Python source on GitHub | TypeScript source on GitHub

Set up your environment

Create a .env file from the example:
cp .env.example .env
Edit .env and fill in your values:
# Required
POLOS_PROJECT_ID=your-project-id   # The Project ID from `polos-server start`
ANTHROPIC_API_KEY=sk-ant-...       # Your Anthropic API key
Install dependencies:
pip install polos-sdk[anthropic] python-dotenv
# Or with uv:
uv sync

Understand the code

The example defines a coding agent with sandbox tools that run directly on your machine, then invokes it with a client that handles approval prompts when the agent wants to execute commands or write files.

Agent definition

The agent gets six built-in sandbox tools (exec, read, write, edit, glob, grep) scoped to a workspace directory. Since there’s no Docker container, destructive operations automatically suspend for your approval. To use Docker, see this example instead.
agents.py
import os
from polos import (
    Agent, max_steps, MaxStepsConfig,
    sandbox_tools, SandboxToolsConfig, LocalEnvironmentConfig,
)

workspace_dir = os.path.join(os.path.dirname(os.path.abspath(__file__)), "workspace")

# Create sandbox tools that run locally on the host
tools = sandbox_tools(
    SandboxToolsConfig(
        env="local",
        local=LocalEnvironmentConfig(
            cwd=workspace_dir,
            path_restriction=workspace_dir,  # prevent file access outside workspace
        ),
    )
)

# Define an agent that can write and run code locally
coding_agent = Agent(
    id="local_coding_agent",
    provider="anthropic",
    model="claude-sonnet-4-5",
    system_prompt=(
        f"You are a coding agent with access to the local filesystem. "
        f"You can create files, edit code, run shell commands, and search the codebase. "
        f"Your workspace is at {workspace_dir}. "
        f"Use the tools to complete the task, then summarize what you did and show the output. "
        f"Always verify your work by running the code after writing it. "
        f"In your final response, include the actual output from running the code."
    ),
    tools=tools,
    stop_conditions=[max_steps(MaxStepsConfig(count=30))],
)
Key things to notice:
  • env: "local" runs tools directly on the host - for container isolation in production, use "docker" instead (see Sandbox Tools)
  • path_restriction / pathRestriction confines file operations to the workspace directory
  • Exec security defaults to approval-always for local mode - every shell command suspends for your approval
  • The agent gets six tools automatically: exec, read, write, edit, glob, grep

Worker

The worker registers the agent and its tools with the orchestrator.
worker.py
import asyncio
import os
from dotenv import load_dotenv
from polos import PolosClient, Worker
from agents import coding_agent, tools

load_dotenv()

async def main():
    client = PolosClient(
        project_id=os.getenv("POLOS_PROJECT_ID"),
        api_url=os.getenv("POLOS_API_URL", "http://localhost:8080"),
    )

    worker = Worker(
        client=client,
        agents=[coding_agent],
        tools=list(tools),
    )

    print("Starting worker...")
    await worker.run()

if __name__ == "__main__":
    asyncio.run(main())

Client with approval handling

The main.py / main.ts file invokes the agent and streams workflow events. When the agent wants to run a command or write a file, Polos suspends execution and emits a suspend event. The client catches the event, shows you what the agent wants to do, and asks for your approval. When you approve, it resumes the agent exactly where it left off. Here’s the core pattern:
main.py (key pattern)
from polos import PolosClient
from polos.features import events

# Invoke the agent
handle = await client.invoke(coding_agent.id, {
    "input": "Create hello.js that prints 'Hello, world!' and run it.",
    "streaming": True,
})

# Stream events - approve each exec/write/edit when the agent suspends
async for event in events.stream_workflow(client, handle.root_workflow_id, handle.id):
    if event.event_type and event.event_type.startswith("suspend_"):
        step_key = event.event_type[len("suspend_"):]
        form = event.data.get("_form", {})
        context = form.get("context", {})
        print(f"\n  Agent wants to: {context.get('command') or context.get('tool', step_key)}")
        approved = input("  Approve? (y/n): ").strip().lower() == "y"
        await client.resume(handle.root_workflow_id, handle.id, step_key, {"approved": approved})
The cloned example includes a richer approval UI with detailed previews of commands, file contents, and path access requests. The pattern above shows the core concept.

Run the agent

Terminal 1 - Start the worker:
python worker.py
You should see:
Starting local sandbox worker...
  Agent: local_coding_agent
  Tools: [exec, read, write, edit, glob, grep]
  Environment: local (no Docker)
  Exec security: approval-always (default for local)
  Press Ctrl+C to stop
Terminal 2 - Invoke the agent:
python main.py
The agent will start working on the task. Each time it wants to write a file or run a command, you’ll see an approval prompt:
============================================================
  WRITE APPROVAL REQUIRED
============================================================

  The agent wants to use the "write" tool:

    Path: /path/to/workspace/hello.js
    Content:
      console.log("Hello from the local sandbox!");

  Approve this operation? (y/n): y

  -> Approved. Resuming...

============================================================
  COMMAND APPROVAL REQUIRED
============================================================

  The agent wants to run a command on your machine:

    Command:   node hello.js
    Directory: /path/to/workspace

  Approve this command? (y/n): y

  -> Approved. Resuming...
Approve each step and the agent will create the files, run them, and report the results.

View in the UI

Open http://localhost:5173 to see your agent execution in the Polos dashboard. You can trace every step, see tool calls, and inspect the agent’s reasoning. The execution trace shows every step of your agent’s run, including LLM calls, tool invocations, and their inputs/outputs: Polos dashboard showing agent execution trace The graph view visualizes the execution flow, making it easy to see how steps relate to each other: Polos dashboard showing agent graph

What just happened?

  1. The worker registered the local_coding_agent and its sandbox tools with the orchestrator
  2. The client submitted a task - the orchestrator called back to the worker to execute the agent
  3. The agent decided to write a file → Polos suspended execution and asked for your approval
  4. You approved → Polos resumed execution exactly where it left off
  5. The agent ran the code in a sandboxed environment with path restrictions
  6. Every step was durably checkpointed - if the process crashed mid-execution, it would resume from the last completed step without re-running the LLM calls you already paid for
This is Polos in action: sandboxed execution, human-in-the-loop approval, and durable state - all without writing retry logic, state machines, or approval infrastructure yourself.

Next steps

Need help? Join our Discord community for support and discussions.