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 exact 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.

When agent_timeout is omitted, the SDK uses a 30-minute Web (non-post) default or a 2-hour post default. These are defaults, not maximums; the server-provided Arena lease is the final resource deadline.

agent_timeout is an optional local limit in seconds. run_agent() applies an explicit value instead of the default. For example, agent_timeout=3600 runs both Web and post Agents for up to 60 minutes. The effective deadline is the earlier of this local limit and the Arena's lease_expires_at. When the local timeout is reached, the SDK signals arena.cancel_event and cancels the Agent callback. It then allows cancellation cleanup and Web output finalization for up to two minutes before submitting; submission happens sooner when cleanup finishes sooner. A post Challenge is submitted with an empty body. A non-post callback may return its output archive during cancellation; if no archive is available by the finalization deadline, the SDK submits a minimal empty final_answer archive instead. The backend closes the Arena after this terminal submission. The returned task outcome records the timeout 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.

Metadata

Release files for agentcyberrange 0.1.3

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for agentcyberrange 0.1.3
File Size Uploaded Provenance
agentcyberrange-0.1.3.tar.gz 53.7 kB Without provenance Details

Built distribution (wheel)

Table of built distributions (wheels) for agentcyberrange 0.1.3
File Interpreter ABI Platform
agentcyberrange-0.1.3-py3-none-any.whl Python 3 none any Details

Total release size: 78.5 kB

Release files / agentcyberrange-0.1.3.tar.gz

Download URL agentcyberrange-0.1.3.tar.gz
Size 53.7 kB
Tags Source
SHA-256 checksum
How to use checksums
4dda8dcdd6c7f5d6d757ed46419c3575a2d74f8ce2d57f35f3f4ad534f78c119
BLAKE2b-256 checksum
How to use checksums
6fd2e4848d19322b7dd829b7ec3978e83173b702c0f37c3b2a0094d42e560eb3
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.13.14

Release files / agentcyberrange-0.1.3-py3-none-any.whl

Download URL agentcyberrange-0.1.3-py3-none-any.whl
Size 24.8 kB
Tags Python 3
SHA-256 checksum
How to use checksums
e72ebc82e2bba4661c8250f55a36dc57d2bdf62ce220f6e30c39f7eea30d2ce8
BLAKE2b-256 checksum
How to use checksums
fb5a1a722d0ddda452b8ebfb891916ff1225856cf25ee9ea026d77bc096d8c20
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.13.14

Security for agentcyberrange 0.1.3

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

No provenance found

None of the files in this release were uploaded with provenance metadata.

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page