Skip to main content
A coding agent with exec security configured as an allowlist. Commands matching the allowlist patterns run immediately; everything else suspends for user approval. Users can reject commands and provide feedback so the agent adjusts its approach.

Configure exec security

import os
from polos import (
    Agent, max_steps, MaxStepsConfig,
    sandbox_tools, SandboxToolsConfig, DockerEnvironmentConfig, ExecToolConfig,
    create_ask_user_tool,
)

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

# Sandbox tools with exec security -- only allowlisted commands run
# without approval. Everything else suspends for the user to decide.
tools = sandbox_tools(
    SandboxToolsConfig(
        env="docker",
        docker=DockerEnvironmentConfig(
            image="node:20-slim",
            workspace_dir=workspace_dir,
            network="bridge",
        ),
        exec=ExecToolConfig(
            security="allowlist",
            allowlist=[
                "node *",   # allow running node scripts
                "cat *",    # allow reading files
                "echo *",   # allow echo
                "ls *",     # allow listing
                "ls",       # allow bare ls
            ],
        ),
    )
)

# Ask-user tool -- lets the agent ask the user questions during execution
ask_user = create_ask_user_tool()

Define the agent

coding_agent = Agent(
    id="secure_coding_agent",
    provider="anthropic",
    model="claude-sonnet-4-5",
    system_prompt=(
        "You are a coding agent with access to a sandbox environment. "
        "You can create files, edit code, run shell commands, and search the codebase. "
        "The workspace is at /workspace inside the container. "
        "Some commands may need user approval before running. If a command is rejected, "
        "read the user feedback in the error output and adjust your approach accordingly. "
        "Always verify your work by running the code after writing it. "
        "If you need clarification or a decision from the user, use the ask_user tool."
    ),
    tools=[*tools, ask_user],
    stop_conditions=[max_steps(MaxStepsConfig(count=30))],
    conversation_history=50,
)

Handle approval events

When the agent tries to run a command that isn’t in the allowlist, the workflow suspends. The client catches the suspend event, shows the command, and collects the user’s decision.
async for suspend in stream_events(polos, handle):
    if suspend["step_key"].startswith("approve_exec"):
        # Show command and ask for approval
        form = suspend["data"].get("_form", {})
        context = form.get("context", {})
        command = context.get("command", "unknown")

        print(f"  Command: {command}")
        approved = input("  Approve? (y/n): ").strip().lower() == "y"

        feedback = None
        if not approved:
            feedback = input("  Feedback: ").strip() or None

        resume_data = {"approved": approved, "allow_always": False}
        if feedback:
            resume_data["feedback"] = feedback

        await polos.resume(
            suspend_workflow_id=handle.root_workflow_id,
            suspend_execution_id=handle.id,
            suspend_step_key=suspend["step_key"],
            data=resume_data,
        )

How it works

  1. The exec tool checks each command against the allowlist patterns
  2. Matching commands (e.g., node hello.js) run immediately
  3. Non-matching commands (e.g., npm install chalk) suspend the workflow
  4. The client receives a suspend event with the command details
  5. If rejected with feedback, the agent reads the feedback and tries a different approach
  6. The ask_user tool lets the agent proactively ask for clarification

Run it

git clone https://github.com/polos-dev/polos.git
cd polos/python-examples/19-exec-security
cp .env.example .env  # Add your POLOS_PROJECT_ID and API key
uv sync
python main.py
The demo task asks the agent to install an npm package (which requires approval) and run node scripts (which are allowlisted). You’ll see the difference between auto-approved and manually-approved commands in action. Open http://localhost:5173 to view your agents and workflows, run them from the UI, and see execution traces. Python example on GitHub | TypeScript example on GitHub