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(client, 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 client.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
uv sync
python worker.py      # Terminal 1
python main.py        # Terminal 2
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