Recurse docs / 09

Run, inspect, and retrieve results

During development, your coding agent uses a direct run to execute one Recurse agent revision with one set of inputs. Keep the run ID, follow the run to a terminal state, and inspect its result and artifacts before deciding what to change next.

The run behavior below requires Recurse SDK 0.2.1 or later. SDK 0.2.0 submits a duration the current service rejects. Use SDK 0.2.3 or later for the restored 512 MiB memory minimum.

Check three outcomes separately:

OutcomeQuestion to answer
Run stateDid execution succeed, fail, time out, or stop?
Task outcomeDoes the result satisfy your constraints and quality target?
DeliverablesDo the result and artifacts provide the evidence you need?

A run can succeed without meeting your task's acceptance target. Use Evaluate and improve to compare revisions. You can use the CLI yourself for any of the steps below.

Start a direct run

Shell
recurse run ./my-agent --inputs inputs.json

inputs.json must contain one JSON object that satisfies the manifest's input schema. The service validates it and applies defaults. Omitting --inputs supplies {}. To read the object from standard input:

Shell
recurse run ./my-agent --inputs - < inputs.json

Recurse packages, uploads, and prepares the project before admitting the serverless run. It writes the admitted run_id to standard output immediately, then appends the outcome to the same YAML document. Wait for the command to finish before parsing the whole document. Running the command again creates another billable run, so inspect the existing run before submitting a replacement.

Runs continue while the account can fund them, up to a fixed 24-hour provider limit with three minutes reserved for finalization. Packaging and preparation happen before that execution window. The CLI waits for the remote outcome without a separate 15-minute cutoff. See Costs and limits for spending guidance.

Direct runs are preemptible by default. To avoid provider Function preemption, add --non-preemptible to recurse run. It costs 3× the Function CPU and memory charge; model tokens, preparation, and Sandbox charges do not receive that premium. Other failures can still occur. This option is for direct runs, not MCP deployments.

Read the run snapshot

Inspect an existing run with:

Shell
recurse status RUN_ID

recurse run and recurse status use the same version-1 YAML snapshot. For example, a model evaluation that saved no downloadable files could report:

YAML
schema_version: 1
run_id: 77777777-7777-4777-8777-777777777777
model: gpt-5.6-luna
status: succeeded
created_at: '2026-09-22T12:00:00Z'
started_at: '2026-09-22T12:00:02Z'
completed_at: '2026-09-22T12:01:00Z'
resources:
  cpu_limit: 1.0
  memory_limit_mib: 1024
cost:
  currency: USD
  total_microusd: 12345
outputs:
  availability: available
  expires_at: '2026-09-23T12:01:00Z'
  result:
    status: succeeded
    trials: 3
    validation_mae: 8.4881
  artifacts: []

Unknown snapshot values are null; the optional error field is omitted when there is no error. outputs.result is the complete object your agent returned. It is separate from outputs.artifacts and from the public error. The model is the service-selected model pinned to the prepared version.

StatusMeaning
queuedWaiting for execution.
runningExecution is in progress.
succeededExecution completed; inspect outputs.result to assess task quality.
failedExecution failed; inspect error.code and its safe message.
timed_outThe execution limit was reached.
cancelledThe service confirms cancellation.
preemptedRecurse detected interruption of a provider attempt. Detection is best effort; inspect external effects before deciding whether to run again.

An internal infrastructure failure appears as status: failed with error.type: engine and error.code: infrastructure_failed. Other failures also have a stable code and safe message. A failed run has outputs.result: null unless the agent completed but artifact storage failed. Files already verified before a handled failure can remain downloadable, so inspect outputs.artifacts as well as error. For guidance, see Troubleshooting.

created_at records admission. started_at is the first execution start, even if the service retries an attempt; completed_at is the terminal time. resources gives the admitted CPU and memory ceilings, not measured utilization. cost.total_microusd is null before completion; afterward it is the settled model and execution-attempt cost in millionths of a US dollar. Preparation cost is separate. For example, 12345 micro-USD is $0.012345.

Inspect native run traces

Read the original model and tool events for an existing direct run or published-app run through the REST API. Reading a trace does not start another billable run. recurse status still returns the run snapshot; the CLI has no trace command, and no SDK upgrade is required for this endpoint.

Set RUN_ID to the existing run ID and RECURSE_ACCESS_TOKEN to a current account access token for its owner. Send that token as a bearer credential. A runtime secret or the device credential stored by recurse login is not an account access token; the CLI does not provide a public token-export command.

API clients obtain access_token from POST https://api.recurse.run/v1/auth/token. Send a JSON body with {"grant_type":"authorization_code","code":"...","code_verifier":"..."} to exchange a login code, or {"grant_type":"device_credential","device_credential":"..."} to renew an existing device credential. Keep credentials private. See installation and access for SDK login and account setup. App-scoped MCP OAuth tokens cannot access this account API.

Shell
curl --fail --silent --show-error \
  -H "Authorization: Bearer $RECURSE_ACCESS_TOKEN" \
  "https://api.recurse.run/v1/runs/$RUN_ID/trace?after=0&limit=20"

The response is raw JSON. For example, a running agent that has completed one tool call could return:

JSON
{
  "run_id": "77777777-7777-4777-8777-777777777777",
  "availability": "pending",
  "expires_at": null,
  "events": [
    {
      "id": 123,
      "dispatch_id": "33333333-3333-4333-8333-333333333333",
      "sequence": 0,
      "producer": "agentia@27c3ca86b0da14e453ae099bd429f174ad4c0659",
      "recorded_at": "2026-10-10T12:00:00Z",
      "event": {
        "type": "framework.agent.events.ToolSuccessEvent",
        "payload": {
          "event_type": "tool_success",
          "tool_name": "write_receipt",
          "tool_call_id": "call_123",
          "arguments": {"item": "tea", "count": 2},
          "result": "2 x tea"
        }
      }
    }
  ],
  "next_after": 123
}

For a nonempty page, use next_after as after on the next request. An empty page has next_after: null: preserve the previous cursor. While execution is active, an empty page means you are caught up, not that the run finished. Poll again with the same cursor and use the run snapshot to check completion. after defaults to 0; limit defaults to 20, with a maximum of 100. Pages stop around 1 MB of event payload, except that one larger event is returned whole. id orders events across attempts; dispatch_id identifies the attempt and sequence orders its events.

Trace availabilityMeaning
pendingThe run is queued or running; records may already be present.
availableThe run finished and captured records are retained.
unavailableNo records were captured.
expiredThe 24-hour window after completion has elapsed; trace rows are deleted.

Cancelled or interrupted runs retain their committed prefix, which may be incomplete. Earlier runtime releases did not retain native traces, so their discarded records cannot be recovered. Access is limited to the account that owns the run; another account receives HTTP 404. For a published app, the caller owns the run. Publishing an app does not give its author access to callers' traces.

Recurse exposes the pinned producer's native serialization without field mapping or content redaction. Records include model request context and results, original tool arguments and results, and errors with their cause chains. Preserve the native payloads; named tuples serialize as arrays. Unsupported values retain payload: null with serialization_error. Python traceback frames and individual provider HTTP retries are not captured; the API is not an HTTP wire dump. Runtime environments and provider authorization headers are not dumped.

Tool arguments, results, and errors can contain application secrets. If a published app emits a publisher-owned credential into those values, its caller can read that credential. Keep secrets out of emitted values and review a trace before sharing it. See Use secrets. The safe public snapshot error and declared application result remain separate from these execution records.

Use the response example and serialization notes above when reading native traces.

Check output availability

Run status and output availability are different. Inspect outputs.availability before using the result or downloading files:

Availabilityoutputs.result and outputs.artifacts
pendingThe run has not finished; both are null.
availableFinalized output is retained. artifacts: [] means no files were produced.
expiredResult content is null and downloads are denied, but artifact metadata remains visible.

Each artifact entry has a public id, relative path, size_bytes, and sha256 digest. outputs.expires_at gives the original retention deadline. Recurse keeps result content and downloadable artifacts for 24 hours after completion. After expiry, recurse status still shows the original deadline and historical artifact metadata, while outputs.result is null and the file bytes cannot be downloaded. A safe terminal error remains visible.

Interpret command exit codes

For automation, read the YAML status instead of assuming every successful CLI request means the run succeeded:

Command resultExit codeStandard output
recurse run confirms any terminal outcome, including failure0One YAML document; inspect status for the run outcome.
recurse status retrieves any valid state0One YAML document.
Either command has an argument, local, authentication, request, or response error1One YAML document with error.type: cli; the run may still be active.
Ctrl-C interrupts either command130One YAML document with error.type: cli; a run's cancellation may remain unconfirmed.

SDK 0.2.1 changed these exit codes: a confirmed failed run now exits 0, and a handled CLI error exits 1. Other commands retain their existing exit codes. A script must inspect YAML status for the remote outcome; && alone no longer proves the run succeeded. The run ID and recovery instructions appear in the same standard-output document. Standard error is reserved for diagnostics.

Inspect an existing run before retrying

If the CLI loses its connection or reports request_failed, the serverless run may continue and incur charges. Inspect the existing run before submitting a replacement. To stop a live run, request cancellation and check its state:

Shell
recurse cancel RUN_ID
recurse status RUN_ID

Cancellation can race with completion. A successful cancellation request does not prove the run stopped; its terminal status does. If admission printed a reference but no run ID, keep that reference for investigation.

Ctrl-C during recurse run requests cancellation and exits 130, even if the service confirms cancellation. If the state cannot be confirmed, use the run ID and recovery commands in the YAML. The CLI does not automatically rerun admitted work. Closing or suspending the terminal does not explicitly cancel the serverless run. See Troubleshooting before approving a replacement when the terminal state is uncertain.

Download and review artifacts

Tools create downloadable artifacts by writing files under recurse.context().workspace. Download them with:

Shell
recurse artifacts RUN_ID --output results

When outputs.availability is available, the command verifies each file's size and SHA-256 digest before placing it in the output directory. Repeating the command safely replaces an existing regular destination file after verification; it rejects unsafe paths and destination directories. If the available inventory is [], the command exits 0 without printing a download.

For pending or expired outputs, the command downloads nothing and reports an error. An expired run still shows artifact metadata through recurse status, but downloads are denied. If transport or verification fails while replacing a file, its existing bytes remain in place. Use Troubleshooting for execution or retrieval errors.