Skip to main content

Firedrill hosted API Python SDK

This package is the Python client for Firedrill Cloud. It complements the local framework; it does not run customer agents or replace their test runner.

Documentation · API reference

python -m pip install firedrill-cloud

Start with the workflow that matches what you are building:

Firedrill and AsyncFiredrill map the hosted control API one-to-one. connect() and connect_async() add owned-session lifecycle and context-manager cleanup. tail_evidence() and tail_evidence_async() follow a world's durable evidence journal, ignore page-boundary duplicates, and fail if a sequence is missing. The default control API is https://api.firedrill.run; use base_url= only when targeting a different environment.

Explicit source and live control

HostedControl(client, project_id) and AsyncHostedControl(client, project_id) add the latest public source and inspection routes without constructing another HTTP client. They retain the supplied client's token supplier, custom transport, headers and request options. These handwritten adapters live outside generated source and return JSON dictionaries with the public camelCase field names.

from uuid import uuid4
from firedrill_cloud import Firedrill, HostedControl

client = Firedrill(token="YOUR_CONTROL_CREDENTIAL")
control = HostedControl(client, "prj_...")
entries = [control.tool_library_entry(name) for name in
           ("tool-mailbox", "tool-work-queue")]
request_key = str(uuid4())  # Persist this and the exact request before dispatch.
created = control.create_source_draft(
    name="My selected Tools", idempotency_key=request_key,
    tools=[{"libraryEntryId": entry["libraryEntryId"],
            "expectedPackageArchiveDigest": entry["packageArchiveDigest"],
            "initialState": "empty"} for entry in entries],
)
draft = created["draft"]
publication = control.publish_source_draft(
    draft["sourceDraftId"], expected_source_digest=draft["sourceDigest"],
    idempotency_key=request_key + "-publish",
)
attempt = control.build_attempt(publication["attempt"]["buildAttemptId"])

The explicit starter selection opts into the listed synthetic rows; empty does not. Publication queues a build, not Tool approval or a running environment. Follow the operation and diagnostics, approve exact Tool artifacts, then use the existing environment/session API. Source mutation retries use the same key and exact request; the helper never retries a mutation under a new key.

tool_library, source_drafts and export_local_reports return one bounded page, with nextCursor when another page exists. source_draft reads exact revision metadata; download_source verifies the original gzip's digest/size (maximum 8 MiB) without extracting, installing or executing it. Deletion needs the exact draft confirmation, version and key; retained build history blocks it.

run_activity(run_id, from_sequence=1, limit=50) reads provisional exact-run activity, not sealed or signed evidence. Reset/expiry can make it unavailable. export_local_reports(export_id, cursor=..., limit=50) pages original uploaded archives separately from hosted bundles; use organization=True explicitly for organization scope. Their provenance remains user-produced and unattested. All corresponding AsyncHostedControl methods are awaited and use the same supplied AsyncFiredrill client, including its asynchronous token supplier.

from firedrill_cloud import Firedrill, tail_evidence

client = Firedrill(token="fdc_...")

for entry in tail_evidence(
    client,
    project_id="prj_...",
    session_id="ses_...",
    max_entries=10,
):
    print(entry.sequence, entry.kind)

Generated source lives in src/firedrill_cloud/generated/, carries its source commit and OpenAPI digest, and must not be edited by hand.

Run a drill with your own agent

An ordinary session, including one configured with a drill, does not itself run your agent or produce a completed drill Result. Create a run from a ready drill-configured session, or attach to an existing run:

For the full create/attach-and-complete workflow, the supplied client needs the exact project's drill.run, run.read, session.read, and project.read permissions. project.read is needed to observe the accepted completion operation through sealing. Session creation and run cancellation require their separate permissions; the helper does not broaden the credential.

import os
import httpx
from firedrill_cloud import Firedrill, run_hosted_drill

with httpx.Client() as transport:
    client = Firedrill(base_url=os.environ["FIREDRILL_API_URL"],
                      token=os.environ["FIREDRILL_CREDENTIAL"], httpx_client=transport)
    run = run_hosted_drill(
        client, project_id=project_id, hosted_run_id=hosted_run_id,
        # Use session_id instead of hosted_run_id to create a run.
        wait_timeout_seconds=1800,
        on_checkpoint=checkpoint_store.save,
        run_interaction=lambda context: my_agent_runner.run(
            task=context.interaction.task,
            world=context.binding,
            cancel_event=context.cancel_event,
            deadline_monotonic=context.deadline_monotonic,
        ),
    )
    print(run.hosted_run_id, run.state)  # Inspect Results for the verdict.

my_agent_runner and checkpoint_store are your own implementations. The runner returns HostedTargetResult or its public JSON object, for example {"schemaVersion": 1, "status": "completed", "output": {"answer": "actual agent output"}, "attachments": []}. It receives world-only access scoped to that interaction, never your control credential. The helper uses exactly your supplied client for orchestration and never runs a model or Tool implementation. Do not fabricate an agent result to mark a drill complete. File uploads are explicit through callback attach or enabled capture methods described below; local paths in TargetResult are rejected.

Explicit files, logs, screenshots, and video

from firedrill_cloud import HostedCaptureFileInput, run_hosted_drill

def run_interaction(context):
    result = my_agent_runner.run(context.interaction.task, context.binding)
    context.attach(HostedCaptureFileInput("artifacts/output.json", "application/json"))
    context.capture.log("Finished the caller-owned interaction")
    context.capture.screenshot(HostedCaptureFileInput("artifacts/screen.png", "image/png"))
    return result

run = run_hosted_drill(
    client, project_id=project_id, hosted_run_id=hosted_run_id,
    root=".", capture={"logs": "always", "screenshots": "retain-on-failure"},
    run_interaction=run_interaction, on_checkpoint=checkpoint_store.save,
)

attach retains an explicitly selected file. Optional capture.file, capture.screenshot, capture.video, and capture.log follow files, screenshots, video, and logs policies: off by default, always, or retain-on-failure. The server applies retention after the drill verdict. Use capture.video(HostedCaptureFileInput("artifacts/recording.webm", "video/webm")) with video enabled for an existing recording. No browser or recording is created automatically, and global console output is not intercepted.

HostedCaptureDriver accepts screenshot, start_video, stop_video, and dispose callbacks. Register with context.capture.register_driver(driver) for the sync helper or await context.capture.register_driver_async(driver) for the async helper. File callbacks return HostedCaptureFileInput; start_video requires stop_video. Hooks receive a cancellation event and monotonic deadline (default 5 seconds; driver_timeout_seconds maximum 60). Synchronous Python hooks must cooperate: they cannot safely be forcibly stopped. Async hooks are awaited with a timeout. Optional capture errors are recorded separately from drill checks.

Paths must be relative to the explicit root, nonempty regular files without symlinks or additional hard links. Limits are 64 MiB/file, 120 MiB/run, 32 files; explicit files accept a bare MIME type (up to 200 characters), including PDF, CSV and ZIP. Logs require plain text, screenshots PNG/JPEG/WebP, and video WebM. Unknown or active file types may download as opaque attachments, not previews. Explicit logs are limited to 16 KiB/message and 1 MiB/4096 messages per interaction. Bytes are copied verbatim; redact secrets before capture. redaction={"status": "applied_by_caller","note":"..."} is caller-provided metadata, not a server redaction guarantee. Original files are never changed or removed.

The helper uploads private immutable snapshots through your exact configured client and authenticated API origin, refusing byte-transfer redirects. Hosted storage is Google Cloud Storage; configured local testing uses the filesystem adapter. An upload checkpoint preserves the snapshot and exact keys so recovery does not rerun the agent; retain its staging directory. A complete checkpoint uses verified server attachment IDs, not local paths. Recovery also preserves the capture root and policies. Caller-produced media is attached to the managed run without claiming Firedrill independently observed your application. Uploading a complete local report remains separate and explicitly unattested.

The high-level helper supplies upload headers automatically. If using the generated raw method after admission, pass exact admitted bytes (not JSON or base64), and retain the same upload key across retries:

client.run_attachments_upload_content(
    project_id, hosted_run_id, attachment_id, request=content_bytes,
    request_options={"additional_headers": {"Idempotency-Key": upload_key}, "max_retries": 0},
)
# A claim-scoped CI credential uses its separate endpoint:
ci_client.ci_attachments_upload_content(
    ci_suite_id, ci_case_id, ci_claim_id, attachment_id, request=content_bytes,
    request_options={"additional_headers": {"Idempotency-Key": upload_key}, "max_retries": 0},
)

The raw method sends application/octet-stream; storage retains the admitted descriptor's media type. The required header is supplied through request options, not a dedicated argument. Missing headers are rejected. Uploading alone does not complete byte verification or the interaction. Async clients use the same arguments with await.

run_hosted_drill_async accepts AsyncFiredrill, an async run_interaction, and an optional async on_checkpoint. Both drivers share one orchestration plan: they wait for each new interaction, submit the actual caller result, and wait through final sealing. Attaching to a terminal run never invokes the agent. Waits default to 30 minutes, bounded to 24 hours; polling defaults to one second. Sync callbacks must honor their cancellation event and monotonic deadline; Python cannot safely forcibly stop an arbitrary synchronous runner. Async callbacks receive deadline cancellation and must cooperate with it.

Persist every checkpoint before its callback returns. After HostedRunRecoveryError, call the helper with project_id, resume=error.checkpoint and your callbacks. A saved complete checkpoint replays the exact TargetResult and key, not the agent. A running checkpoint has an unknown caller outcome and is deliberately not replayed; recover the actual result from your runner, then submit it at the saved run/interaction/revision. Async cancellation retains checkpoint on CancelledError and is not swallowed. Checkpoints contain no world credential, but can contain sensitive agent output; store them privately. Python checkpoints use Python request field names and should be resumed through the Python helper, not passed to another SDK's checkpoint API.

For a manual terminal workflow, the separately installed public CLI extension provides firedrill cloud run create, interaction, connect --format env, complete --result FILE, and get. Use the exact interaction/revision and retry command printed by the CLI; your runner still owns all agent execution.

Optional warm worlds

HostedProject(client, project_id).create_session(environment_id=..., seed=..., idempotency_key=..., warm_pool="prepare") prepares one short-lived exact world without issuing access. warm_pool="prefer" checks out an exact prepared setup or creates cold. AsyncHostedProject.create_session has the same async surface. Omitting the option preserves ordinary cold creation. Keep build, seed, scenario/drill and receiver selection identical for a matching checkout.

Prepared sessions remain warming until checkout; a warmPool.state of ready does not authorize an agent connection. Inspect their returned expiry and metadata (session.model_dump(by_alias=True)["warmPool"]). The server bounds lifetime and capacity and rechecks authority at checkout. Owned connect/connect_async and HostedProject.start accept only warm_pool="prefer", since they return usable world access; preparing alone uses create_session.

Owned-session quickstart

Your existing runner owns the agent and the HTTP client. The helper creates one exact hosted session, waits for its operation and ready state, and issues the selected actor's short-lived binding. Omit scenario_id and drill_id to serve the build baseline; supplying both is an error. No agent is run automatically.

import os
import httpx
from firedrill_cloud import Firedrill, connect

with httpx.Client() as http:
    client = Firedrill(token=os.environ["FIREDRILL_CREDENTIAL"], httpx_client=http)
    with connect(
        client,
        project_id="prj_...",
        environment_id="env_...",
        actor_id="operator",  # an actor declared in your selected build
        seed="42",
        # scenario_id="busy",  # optional named setup
        # build_hash="sha256:...",  # optional exact immutable build
        operation_timeout_seconds=120,
    ) as world:
        binding = world.binding
        # Pass binding.world_http_url or binding.world_mcp_url and its credential
        # to your agent's existing test configuration. Never pass client/token.
        # Invoke your agent through your own runner here.
        world.reset()  # initial setup restored; use world.binding again afterward

The async equivalent has the same options and methods:

import os
import httpx
from firedrill_cloud import AsyncFiredrill, connect_async

async def test_world():
    async with httpx.AsyncClient() as http:
        client = AsyncFiredrill(token=os.environ["FIREDRILL_CREDENTIAL"], httpx_client=http)
        async with await connect_async(
            client, project_id="prj_...", environment_id="env_...",
            actor_id="operator", seed="42",
        ) as world:
            receipt = await world.advance_to(1_000_000, max_events=100)
            # Inspect receipt.status: interrupted/failed is not completed.
            assert receipt.status == "completed"

Context exit destroys the exact owned session on success, exceptions, and cancellation. connect() without with (or connect_async() without async with) transfers cleanup responsibility to you: call world.destroy() or await world.destroy(). The helper never closes the supplied client, installs signal handlers, changes your runner, or cancels a server operation implicitly. Stop or join agent work before leaving the context; retaining an old binding does not retain an active world.

Control and recovery

Handle method Result
reset(packages=...) or reset(hosted_snapshot_id=...) Restore selected state and rotate the binding; omit both for a full reset
extend(ttl_ms) Return the server's session expiry; does not renew access credentials
advance_to(target_us, max_events=...) Return the canonical durable time receipt, including partial/interrupted outcomes
set_fault(package_id, fault_id, active) Return the canonical durable fault-control receipt
destroy() Await destruction; repeated calls after success are no-ops
resume_pending() Resolve the exact captured uncertain mutation and return its original result

Every mutation optionally accepts idempotency_key and a threading.Event as cancel_event. The helper generates a key when omitted. Mutations through one handle are serialized. Cancellation of async tasks remains CancelledError; synchronous cancellation uses cancel_event or the caller's ordinary KeyboardInterrupt. Local cancellation does not mean the server stopped.

On an uncertain network error, timeout, cancellation, malformed receipt, or selection mismatch, world.pending_mutation retains the exact request, key and known operation ID. It contains no credentials. Other mutations and new binding reads are blocked until that attempt is resolved. Call resume_pending() to poll the known operation, or replay the original request with the same key if its acknowledgement was lost. Never create a new key to retry uncertain work. An initial canonical API rejection or terminal failed/cancelled operation is a known outcome and releases that guard; later authorization failures cannot erase an already uncertain attempt.

try:
    world.extend(3_600_000)
except Exception:
    if world.pending_mutation is not None:
        print(world.pending_mutation.operation_id)  # safe recovery identifier
        world.resume_pending()  # exact original key, input, lease, and version
    else:
        raise

Context exit first resolves a pending mutation, then attempts destruction under a fresh cleanup deadline. It never overwrites a pending intent with a different lease. Cleanup failure is raised on an otherwise successful exit; when your code already raised, that primary exception is preserved and its cleanup_error attribute, also available as world.cleanup_error, reports the cleanup failure. Inspect it and recover explicitly; expiry is the final safety net, not a guarantee that cleanup succeeded. Repeated async cancellation is shielded while bounded cleanup finishes.

Acquisition failures expose error.pending_creation, including the original key and request. If creation was acknowledged, cleanup is best effort by default (cleanup_on_failure=False explicitly disables it). If the create response was lost, no resource ID is known: the helper never lists sessions or guesses what to destroy. Retry connect() with the original options and idempotency_key=error.pending_creation.idempotency_key to recover that exact server-side intent. Persist recovery details if your runner must survive process termination; handles and pending attempts are in-memory only.

FiredrillLifecycleError.kind identifies SDK orchestration failures, and .operation retains an available canonical operation/error/receipt. Generated HTTP errors remain ApiError with their server envelope; transport exceptions remain HTTPX exceptions. Recovery attributes are additive. Neither exception text nor session readiness is an agent-test verdict.

Deadlines cover acquisition and each mutation, including polling (default 120 seconds, polling every 0.5 seconds). Every HTTP request disables generated automatic retries and receives the remaining timeout. Async requests are also bounded by the lifecycle clock. Sync cancellation is observed between requests and during polling; an in-flight sync request relies on HTTPX's phase timeouts. A custom synchronous transport that ignores timeouts, or a continuously streaming response, cannot be forcibly preempted. Keep the standard bounded control API/transport contract; abrupt process termination cannot run cleanup.

Start synthetic tools without a drill

sessions_create() accepts the build baseline (no selector), a named scenario_id, or a drill_id. The selectors are mutually exclusive. Starting a session serves the synthetic environment; it does not run or host your agent.

created = client.sessions_create(
    "prj_...",
    "env_...",
    idempotency_key="tools-baseline-001",
    seed="42",
    # scenario_id="busy",  # optional; omission uses the build baseline
)

This returns a session and an asynchronous operation, not a ready binding. Poll operations_get(project_id, operation_id) with a bounded deadline; only after it succeeds and sessions_get() returns ready, call sessions_issue_access(..., actor_id=...) and pass that short-lived binding to your agent. Failed operations carry their canonical error envelope. The same flow is available with await on AsyncFiredrill.

Use sessions_reset() to restore the selected initial setup or an explicit snapshot, then obtain a new binding: old credentials are fenced by the changed lease generation. Destroy the exact session with sessions_destroy() in your cleanup path. Mutations require stable idempotency keys; after a timeout, retry the same key and arguments or inspect the known operation, never assume the remote work stopped. These generated methods do not automatically provision, poll, reset or clean up on behalf of the caller; use the handwritten lifecycle helpers above when you want owned-session orchestration.

sessions_extend(project_id, session_id, idempotency_key=..., expected_lease_generation=session.lease_generation, expected_version=session.version, ttl_ms=3_600_000) requests a deadline one hour from server admission time. The session must be ready and unexpired, and the new deadline must be later than its current one and within your allowance. The response contains the session and an already-succeeded operation. Retry the same key and arguments after interruption: a replay does not extend time again. Existing access credentials and drill timeouts keep their original expiry. AsyncFiredrill exposes the same operation with await.

Saved browser tests and run evidence

These groups support AsyncFiredrill too (await, or async for for streams). Use project-authorized control credentials, never world bindings. Retain each mutation's exact key, body and expected versions before sending; retry uncertain work unchanged. Example keys need replacing for new intents, and persisting for cross-process recovery; generated methods retain no pending intent or cleanup. Targets require public HTTPS on port 443. Keep credentials out of definitions, URLs/tasks/messages. Agent input, runtime parameters (including resolved parameterSecretRefs), fill actions and URL values suppress requested captures unless the request explicitly sets captureConsent="include-sensitive-content" (capture_consent in Python). Consent permits recording potentially sensitive content; it is not a redaction guarantee. Screenshot masks also apply to live frames, but do not redact video, traces, network traffic or values shown elsewhere.

from pathlib import Path
from firedrill_cloud.generated import CreateBrowserRunRequestTestId
from firedrill_cloud.generated.browser_tests import CreateBrowserTestRequestDefinition

project_id, browser = "prj_...", client.browser_tests
request_options = {"max_retries": 0, "timeout": 30}
# Your canonical browser-test JSON, using synthetic test data.
definition = CreateBrowserTestRequestDefinition.model_validate_json(Path("smoke.browser.json").read_text())
saved = browser.browser_tests_create(
    project_id, idempotency_key="browser-save-001", definition=definition,
    request_options=request_options,
).test
run_request = CreateBrowserRunRequestTestId(
    test_id=saved.test_id, expected_test_version=saved.version, mode="replay", timeout_ms=120_000,
)
run = browser.browser_runs_create(
    project_id, idempotency_key="browser-run-001", request=run_request,
    request_options=request_options,
).run
run_id = run.run_id  # retain for recovery
events = browser.browser_runs_list_events(project_id, run_id, after=0, limit=100)
for event in events.items:
    print(event.sequence, event.type, event.message)
after = events.items[-1].sequence if events.items else 0
run = browser.browser_runs_get(project_id, run_id)
print(run.state, run.result)
if run.artifact is not None:
    with Path(f"{run_id}.tar.gz").open("xb") as output:
        for chunk in browser.browser_runs_download_artifact(project_id, run_id):
            output.write(chunk)

Poll with a bounded deadline; retain the last event sequence across empty pages. queued/running are active; other states terminal. completed is not a passing assertion verdict, and browser results never verify world state. Downloads are integrity-checked bundles. browser_tests_list() uses opaque next_cursorafter.

For follow-up, create a separate mode="agent", interactive=True run with a saved task and authoring.run permission. Use that active run's ID here:

from firedrill_cloud.generated import BrowserRunMessageRequest_Message, BrowserRunMessageRequest_Finish

interactive_run_id = "brun_..."  # exact active interactive run
browser.browser_runs_send_message(
    project_id, interactive_run_id, idempotency_key="browser-follow-up-001",
    request=BrowserRunMessageRequest_Message(message="Check the confirmation panel."),
    request_options=request_options,
)
browser.browser_runs_send_message(
    project_id, interactive_run_id, idempotency_key="browser-finish-001",
    request=BrowserRunMessageRequest_Finish(), request_options=request_options,
)
# Alternatively, cancel this exact run, then poll its terminal state:
# browser.browser_runs_cancel(project_id, interactive_run_id, confirm=interactive_run_id,
#     idempotency_key="browser-cancel-001", request_options=request_options)

Acceptance is not completion; finish closes input for independent assertions. browser_runs_rerun(project_id, run_id, confirm=run_id, idempotency_key=...) pins original inputs, excluding prior messages. A new rerun gets a new key; retries do not.

High-level hosted projects

HostedProject and AsyncHostedProject reuse the exact configured generated client, including credentials, base URL, and transport. They never open a second client or upload local files.

from firedrill_cloud import Firedrill, HostedProject, HostedHistoryFilters

client = Firedrill(base_url=api_url, token=control_credential)
project = HostedProject(client, project_id)
tools = project.tools(build_hash)  # Contract metadata; no baseline records.
binding = project.connect(session_id=session_id, actor_id=actor_id)
page = project.history(HostedHistoryFilters(outcome="failed"), limit=20)
result = project.results(hosted_run_id)

connect_session(client, ...) and its async counterpart attach to an existing ready session without creating/resetting/destroying it. Only give their returned actor-scoped world binding to your external runner, never the control credential. After WorldAccessUnresolvedError, retry with its exact idempotency_key and original input. project.start(environment_id=..., actor_id=..., seed=...) instead creates an owned lifecycle handle; use its context manager for cleanup. Omit scenario/drill selectors for baseline. Selecting a drill setup does not run it.

History filters operate across all retained server history. Preserve them when following cursors; timestamps use inclusive created_from_ms and exclusive created_before_ms. Results preserve canonical verdict, runner, and orchestration outcomes rather than treating admitted work as completed.

Compare retained runs

The same configured client supplies both summary comparison and one bounded page of original recorded details:

project = HostedProject(client, project_id)
summary = project.compare(
    baseline_hosted_run_id=baseline_id, candidate_hosted_run_id=candidate_id,
)
page = project.compare_details(
    baseline_hosted_run_id=baseline_id, candidate_hosted_run_id=candidate_id,
    kind="assertions", limit=10,
)

AsyncHostedProject exposes the same methods with await. Lower-level clients provide runs_compare and runs_compare_details. Kinds are assertions, state_changes, and operations; page size defaults to 10 and is capped at 25. Pass next_cursor as cursor with the same project, pair and kind to continue; an expired cursor needs a fresh first-page request. Helpers never fetch every page automatically or create a second credential context.

Matching summary measures do not guarantee identical recorded values. Missing entries do not prove deletion; oversized values are explicitly omitted, not partially displayed. Comparisons describe recorded differences without reevaluating the agent or declaring an improvement.

Prepare source for Python callers

This Python package does not provide the TypeScript prepareHostedDrill or createHostedCliBridge convenience helpers. To use test-local setup, explicitly publish a derived build with the shared CLI:

firedrill cloud publish --project "$PROJECT_ID" --root . \
  --idempotency-key derived-review-001 --drill review-case --setup ./setup.json

Use your source's actual drill ID and canonical data-only setup JSON. Review and explicitly approve any new Tool artifacts, then pass the returned exact build hash to Python session creation (build_hash) or run_hosted_drills. Publication does not automatically approve Tools or promote an environment. Your Python agent can use the issued HTTP/MCP world binding directly; the caller-owned CLI bridge is a separate TypeScript helper, not a Python API or agent source rewrite.

Run multiple hosted drills

run_hosted_drills and run_hosted_drills_async use your configured client and existing caller-owned agent. They create ordinary sessions/runs, not a managed suite resource, and never approve Tools or promote an environment.

from firedrill_cloud import run_hosted_drills

results = run_hosted_drills(
    client, project_id=project_id, environment_id=environment_id,
    build_hash=build_hash, drills=["review-message", "reject-write"],
    trials=2, retries=1, concurrency=2, seed="42",
    root=".", capture={"logs": "always"},
    on_checkpoint=save_checkpoint_atomically,
    run_interaction=run_my_existing_agent,
)
print(results["observed"])

Select suite="suite-id" instead of drills to use declared drill IDs/tags, or omit both for every drill in the exact build. Explicit options override suite defaults; otherwise trial count comes from the suite or drill. The selected build must already be approved for the environment. Each attempt creates a fresh session and destroys that owned session after its terminal result; all ordinary run/report IDs remain in Results/history. Required project scopes are build.read, project.read, session.create, session.read, session.destroy, drill.run and run.read.

Retries occur only after known nonpassing terminal outcomes. Mixed retry outcomes yield an inconclusive logical trial. Returned observed counts are raw observations, not confidence estimates or signed suite verdicts. Within each drill, seeds increment from the selected unsigned 64-bit seed modulo 2^64; retries retain that trial's seed. Every attempt links its own evidence bundle.

Persist checkpoint callbacks atomically in private storage; callback saves are serialized. HostedDrillsRecoveryError.checkpoint retains exact mutation keys, staged uploads and pending session/run identities. Resume the same selection with resume=checkpoint. An uncertain running callback is never replayed; reconcile its real outcome through single-run recovery first. Terminal batch resume does not invoke the agent. Python checkpoints are language-specific and must not be passed to the TypeScript helper.

Sync callbacks can run concurrently on worker threads and must cooperate with their cancellation event/deadline; they cannot safely be forcibly killed. Async callbacks and checkpoint writers must be awaitable. Use the async helper inside an asyncio loop. The default wait limit is 30 minutes (maximum 24 hours). Planning is bounded to 1,000 logical trials, 10,000 possible attempts and concurrency 64; project quotas still apply. Your own runner owns file watching and scheduling. To shard jobs, pass disjoint explicit drill lists with separate checkpoints; intentional repeat runs use new batches and explicit build identities. No source upload, compilation, approval or background watch starts implicitly.

Local verification

Install with python -m pip install -e '.[dev]' and run python -m pytest. The full repository test gate includes generated-client contracts, evidence tailing, and sync/async lifecycle through a real loopback HTTP server. Browser, CI and source-proposal contracts also exercise real HTTP, typed SSE, binary downloads, exact-key retries and canonical failures. These fixtures script control responses; they prove client serialization, parsing, polling, identity checks, cleanup and recovery, not world-engine fidelity, customer-agent behavior, live hosted authorization, or deployed availability. The same tests can run from outside the checkout against a freshly installed wheel using python -m pytest --import-mode=importlib /absolute/path/to/this/repository/tests.

Export scenario seed source

export_session_scenario(client, project_id, session_id, scenario_id="captured-state", expected_lease_generation=lease, include_sensitive_data=True) and export_session_scenario_async(...) return canonical scenario JSON under scenario, its digest and exact session/build/lease identity. Optional packages=[...] selects Tools. The caller must explicitly acknowledge raw unredacted data and review it before saving or sharing. These helpers do not write files or mutate/publish a world. Source contains Tool records and baseline deletions, not actors, clock, faults, pending work or history; unselected Tools inherit the destination baseline. Stale/unauthorized reads and exports over 50,000 actions or 1 MiB fail without truncation.

list_hosted_snapshots(client, project_id, session_id=..., limit=..., cursor=...) returns one bounded metadata page; get_hosted_snapshot(client, project_id, hosted_snapshot_id) resolves one checkpoint. Both have _async equivalents. artifactAvailability: "not_checked" is explicit: metadata discovery does not verify stored bytes, and restore/fork performs that validation. No storage paths or credentials are included.

Apache-2.0 · Copyright Reload Tech Inc.

Release files for firedrill-cloud 0.1.2

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

Source distribution (sdist)

Source distribution for firedrill-cloud 0.1.2
File Size Uploaded
firedrill_cloud-0.1.2.tar.gz 791.5 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for firedrill-cloud 0.1.2
File Interpreter ABI Platform
firedrill_cloud-0.1.2-py3-none-any.whl Python 3 none any Details

Total release size: 4.1 MB

Release files / firedrill_cloud-0.1.2.tar.gz

Download URL firedrill_cloud-0.1.2.tar.gz
Size 791.5 kB
Tags Source
SHA-256 checksum
How to use checksums
e5e9bb7591039b00fcead762033e80013f42dd6cc6417ef3485e7fd9d9197181
BLAKE2b-256 checksum
How to use checksums
59c941199378341899aa1d71004f515fbec6819ea3d482b155a0278fdf227263
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

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.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 21, 2026.

Transparency log

Release files / firedrill_cloud-0.1.2-py3-none-any.whl

Download URL firedrill_cloud-0.1.2-py3-none-any.whl
Size 3.3 MB
Tags Python 3
SHA-256 checksum
How to use checksums
d85aeecf9fb7d54f249704f798ad6770674af84a4ec8efb7d1cc4edf909b6650
BLAKE2b-256 checksum
How to use checksums
6fee0d714d03a306ad6fb88e9315036dce3954f9c62ef2051ea307f2080e5987
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

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.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 21, 2026.

Transparency log

Release history Release notifications | RSS feed

This release

0.1.2 This release

2 release files

0.1.1

2 release files

0.1.0

2 release files

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