Skip to main content
plurai-run-simulation is the runner behind /simulation-run. It plays each scenario of a scenario set against your harness: a simulated user talks to your agent over the scenario’s seeded world, the mock environment serves your agent’s tool calls, and the backend scores every eval. Run it yourself to score a suite from CI or a script. Scenarios and evals are referenced by id. The runner needs only your simulate.py, not a local copy of the scenarios.

Before you start

  • The SDK is installed in your agent’s environment. See Install the SDK.
  • You have built a suite, so your work folder holds plurai_sim/.scenario_set_id, plurai_sim/.eval_set_id, and plurai_sim/.mock_service_url for the deployed mock environment.
  • You are signed in (plurai login --status), or PLURAI_URL and PLURAI_TOKEN are exported. The Run refuses to start without one.

Run the suite

Omit --scenario-id to run every scenario in the set. Repeat it to run only some:
The runner reaches the backend you signed in to, or the one PLURAI_URL names. There is no URL or token flag. Without --eval-set-id the Run plays every conversation but scores nothing, and the runner says so at the end.

What it does

  1. Checks that the backend is alive and accepts your token, and that the mock environment answers.
  2. For each scenario, opens a conversation on the backend and starts your harness in a child process.
  3. Seeds the scenario’s world and context, then alternates turns: your agent answers through invoke_agent, and the backend’s simulated user replies.
  4. When the conversation ends, sends the transcript, tool events and final world state to the backend, which scores every eval.
  5. Writes the per-scenario artifacts and a run_summary.json, and prints a summary line.

Flags

Required

What to run

Mock environment

Budgets and retries

Regressions

Recording and identity

Checks

Outputs

Each scenario gets a folder <out>/<scenario_id>/: The Run folder also holds run_summary.json: the per-scenario status and verdict, scores, the trials block for --trials, the stability annotations, and the turn cap with its source. A stability retry is kept in the scenario’s stability_retry/ folder; a turn-cap re-run keeps the superseded attempt under previous_attempt/.

Exit codes

plurai-run-simulation, plurai and plurai-scan-contract share one exit code table. A code means the same thing everywhere, and each command uses only the rows that apply to it. The names are constants in plurai.exit_codes, and run_summary.json carries the same number in its exit_code field. 2 is never returned.

When a Run stops early

  • A mock that cannot answer (11). The token is rejected, the mock environment is not deployed, or the URL is not a mock. Nothing new starts, no score is reported, and stderr names what to change. A dropped connection is retried and does not stop the Run.
  • Credit (20). When your organization is out of credit, nothing new starts. Scenarios that already finished keep their verdicts; the rest are recorded as incomplete, never as failed. stderr says what happened and, when a payment fixes it, points to your billing page. Before stopping, a scenario that is only waiting to start can wait for one of the Run’s own conversations to end.
  • Capacity or an outage (20). If Plurai is in high demand or unavailable, the Run first waits and retries (a short backend restart passes unnoticed). If it stays down, the Run stops with one plain sentence, such as “Plurai is unavailable right now. Please try again in a few minutes.”
In each case run_summary.json carries an aborted block with the reason, and a stopped Run does not update .last_run. To fail CI on eval results, fail on exit code 1, or read the verdicts from run_summary.json.

Run from your own Python code

plurai.orchestrator.Orchestrator drives a single scenario from Python. It is part of the supported surface, but the command-line runner is the recommended entry point: it adds the preflight checks, trials, stability retries and the summary. See the API reference.

Next steps

Run in CI

Run the suite on every change.

Write a harness

The simulate.py contract the runner drives.