Skip to main content

Agent Cyber Range SDK

agentcyberrange is the Python SDK for running autonomous Agents against the Agent Cyber Range evaluation platform. It discovers the available Challenges, keeps a bounded number of Arenas running, waits for capacity, and submits each Arena when the Agent finishes or reaches its deadline.

Access to the evaluation service requires a Task Token issued by Agent Cyber Range.

Python 3.11 or newer is required.

Install

Install the latest release from PyPI:

python -m pip install agentcyberrange

For local development from a repository checkout:

uv sync --extra dev

Quick start

Set the Task Token issued by Agent Cyber Range:

export AGENTCYBERRANGE_TASK_TOKEN='replace-with-secret'

Then provide one asynchronous Agent function:

import asyncio
import os

from agentcyberrange import (
    AgentCyberRangeClient,
    AgentOutputPath,
    ArenaHandle,
    run_agent,
)


async def run_your_agent(arena: ArenaHandle) -> AgentOutputPath | None:
    # Replace this body with your Agent implementation.
    print(arena.task_prompt)
    print(arena.entry_urls)
    agent_output: AgentOutputPath | None = None
    return agent_output

async def main() -> None:
    async with AgentCyberRangeClient(
        base_url=os.environ.get(
            "AGENTCYBERRANGE_BASE_URL", "https://eval.agentcyberrange.io/"
        ),
        task_token=os.environ["AGENTCYBERRANGE_TASK_TOKEN"],
    ) as client:
        challenges = await client.list_challenges()
        # Replace these with one or more specific Challenge IDs if needed.
        challenge_ids = [challenge.challenge_id for challenge in challenges[:5]]
        result = await run_agent(
            run_your_agent,
            max_concurrency=2,
            challenge_ids=challenge_ids,
            client=client,
        )
    print(result.model_dump_json(indent=2))


asyncio.run(main())

The Quick Start keeps the main customization points visible: the Agent callback, the server-provided Prompt, and two concurrent Agents. The SDK returns the available Challenge list, and the example selects the first five for this run. It leaves agent_timeout unset, so Web Challenges use the 30-minute default and post Challenges use the 2-hour default. Pass agent_timeout directly to apply one shorter limit across the selected Challenges.

Environment variables are reserved for connection configuration (AGENTCYBERRANGE_TASK_TOKEN and, optionally, AGENTCYBERRANGE_BASE_URL). Per-run policy such as concurrency, Agent timeout, model, and permission mode stays explicit in Python arguments or CLI flags instead of hidden process-global configuration.

Fleet and the SDK both limit a Web (non-post) Arena to 30 minutes and a post Arena to 2 hours. These values are the SDK's defaults and hard maximums. When agent_timeout is omitted, the SDK selects the limit from the Challenge type.

agent_timeout is an optional local limit in seconds. run_agent() applies the same requested value to every selected Challenge, then caps it at the type-specific maximum. For example, one hour becomes 30 minutes for Web and remains one hour for post; three hours becomes 30 minutes for Web and 2 hours for post. The effective deadline is the earlier of this local limit and the individual Arena's authoritative server deadline. When it is reached, the SDK signals arena.cancel_event and cancels the Agent callback. A post Challenge is submitted with an empty body. A non-post callback may finish its cancellation cleanup by returning an output archive, which the SDK submits immediately; if cancellation produces no archive, the SDK submits a minimal empty final_answer archive instead. A timeout or deadline therefore always triggers a terminal submit rather than abandoning the Arena with a close request. The returned task outcome records the timeout or deadline in error_code and error_message.

on_event receives lifecycle and retry events. Capacity waits include the HTTP status, error code, request ID, Retry-After, and attempt number in SchedulerEvent, so callers can report an automatic wait without disabling it. Pass a RetryPolicy to run_agent() when a finite capacity_wait_timeout is required.

arena_ready_delay adds a cancellable settling period after Fleet reports an Arena as running and before the Agent callback starts. This is useful when an exposed service needs a few extra seconds to begin accepting connections.

The SDK automatically:

  • discovers all currently available Challenges;
  • keeps at most max_concurrency Work Slots active;
  • waits and retries when the control plane has no free capacity;
  • refills a Work Slot as soon as an Arena is submitted;
  • submits a post Challenge when the Agent returns, raises, times out, or reaches the server deadline;
  • submits a non-post Challenge with the returned output archive, or with a minimal empty final_answer archive when the Agent returns no path or raises.

Run Codex

Install and authenticate the Codex CLI, set the Agent Cyber Range Task Token, then run:

uv run python examples/codex_cli.py

The example uses the default SDK settings and Codex model. It starts a non-interactive, ephemeral codex exec session in a temporary workspace and enables network access inside the workspace-write sandbox. See the Codex non-interactive mode documentation.

The Codex example passes the server-provided arena.task_prompt to Codex unchanged. It removes AGENTCYBERRANGE_TASK_TOKEN and AGENTCYBERRANGE_BASE_URL from the child environment, so Codex cannot directly operate the control plane. If Codex creates a final_answer/ directory for a non-post Challenge, the callback archives it under .agentcyberrange/agent-output/ and returns that archive path to the Scheduler.

When an SDK timeout or Arena deadline cancels the callback, the example first stops Codex and then archives any existing final_answer/ before the temporary workspace is removed. The Scheduler submits that archive immediately. If the directory does not exist, the Scheduler submits a minimal empty final_answer archive instead. A post Challenge returns None and is submitted with an empty body.

Low-level client

Use AgentCyberRangeClient when you need to manage one Arena manually:

import asyncio
import os

from agentcyberrange import AgentCyberRangeClient


async def main() -> None:
    async with AgentCyberRangeClient(
        base_url=os.getenv(
            "AGENTCYBERRANGE_BASE_URL",
            "https://eval.agentcyberrange.io/",
        ),
        task_token=os.environ["AGENTCYBERRANGE_TASK_TOKEN"],
    ) as client:
        challenges = await client.list_challenges()
        if not challenges:
            raise RuntimeError("Agent Cyber Range has no available Challenges")
        arena = await client.create_arena(challenges[0].challenge_id)
        print(arena.task_prompt)
        print(arena.entry_urls)
        result = await client.submit(arena.arena_id)
        print(result.verdict)


asyncio.run(main())

create_arena() automatically waits on capacity_exhausted and reuses the same Idempotency-Key across retries. The client honors standard proxy environment variables; set trust_env=False when a localhost backend must bypass them.

submit() also accepts a .tar.gz or .zip path for a non-post Challenge:

result = await client.submit(
    arena.arena_id,
    agent_output="outputs/agent-output.zip",
)

The SDK resolves a relative output path against the process working directory when submit() starts, then reads the file once so an ambiguous HTTP result can safely retry the same archive. The file must be non-empty and no larger than 10 MiB.

With run_agent(), return the archive path when a non-post Agent produced one. Returning None is valid for both Challenge types: the Scheduler uses an empty request body for a post Challenge and a minimal empty final_answer archive for a non-post Challenge.

from agentcyberrange import AgentOutputPath, ArenaHandle


async def run_your_agent(arena: ArenaHandle) -> AgentOutputPath | None:
    agent_output = await your_agent(arena)
    return agent_output

Examples

Development verification

uv sync --extra dev
uv run pytest
uv run ruff check .
uv run mypy src
uv build

scripts/live_backend_smoke.py exercises every low-level client method. scripts/live_scheduler_smoke.py verifies rolling refill and capacity fallback. scripts/run_agent_test.py runs mock or external Agent processes against the deployed control plane and asserts that no active Arena is left behind.

For a repository-only live Agent test:

uv run python scripts/run_agent_test.py

To replace the mock Agent with another process:

uv run python scripts/run_agent_test.py \
  --agent-command 'codex exec --ephemeral --skip-git-repo-check --sandbox workspace-write -c sandbox_workspace_write.network_access=true -'

The default base URL is https://eval.agentcyberrange.io/. Override it with AGENTCYBERRANGE_BASE_URL when testing another deployment.

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

agentcyberrange-0.1.1.tar.gz (53.4 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

agentcyberrange-0.1.1-py3-none-any.whl (24.5 kB view details)

Uploaded Python 3

File details

Details for the file agentcyberrange-0.1.1.tar.gz.

File metadata

  • Download URL: agentcyberrange-0.1.1.tar.gz
  • Upload date:
  • Size: 53.4 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.1.0 CPython/3.13.14

File hashes

Hashes for agentcyberrange-0.1.1.tar.gz
Algorithm Hash digest
SHA256 da97040265a60a08bb81098c370be0a7784edc902ebd6d2723b297e3a3602403
MD5 aff1e2a9b6c84afac8c1b849022a364c
BLAKE2b-256 f6e42ff1fd2f1fff197a189da51254c6c475501bd9729c55b19be71ececfc77b

See more details on using hashes here.

File details

Details for the file agentcyberrange-0.1.1-py3-none-any.whl.

File metadata

File hashes

Hashes for agentcyberrange-0.1.1-py3-none-any.whl
Algorithm Hash digest
SHA256 1a808c1e0e738bc91377b222d55a16520ce1fcfd682a8fc792b8fa68ef5b193a
MD5 84ea6ee12d05b5ba8ce43584e8ce103c
BLAKE2b-256 1b26b9391341147656e28fe0e50166952c569ebeffc35567c25088381801e360

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page