Instance Lifecycle
An instance moves through a well-defined state machine from creation to terminal state. Understanding the lifecycle is essential for building reliable consumers — dashboards, CI integrations, and agent interfaces all need to handle the full transition graph.
State machine
Section titled “State machine”Status values
Section titled “Status values”| Status | Terminal? | Description |
|---|---|---|
created |
No | Instance record persisted; container not yet provisioned |
starting |
No | Container launching; setup commands running; worker initializing |
running |
No | Resolver is actively executing inside the container |
awaiting_input |
No | Resolver wrote an input request and is waiting for a response |
paused |
No | Resolver execution paused; resumable via POST /resume |
completed |
Yes | Resolver finished successfully |
failed |
Yes | Resolver encountered an unrecoverable error |
cancelled |
Yes | Instance was cancelled via DELETE or POST /stop |
The happy path is: created → starting → running → completed.
Phase progression
Section titled “Phase progression”Each resolver defines its own phase progression. The host platform does not
prescribe phases — it tracks status transitions and exposes phase through
the state snapshot and events.
Common patterns across resolver implementations:
The exact phases, their names, and durations vary by resolver. Read each resolver’s documentation for its specific phase taxonomy.
Input requests — recovery flow
Section titled “Input requests — recovery flow”When a resolver cannot proceed without human input, it writes an A2UI input request
and the instance transitions to awaiting_input.
Pause / resume
Section titled “Pause / resume”paused is for intentional checkpoints — the resolver decides to stop and persist
state for later resumption. Unlike awaiting_input, there is no pending request to
respond to. You resume explicitly.
Requirements:
- Resolver returns
ResolverResult(status="paused")fromrun() - Resolver’s
supports_resumemust beTrue - Resolver must have saved checkpoint state before returning
paused
# Resume a paused instancecurl -X POST -H "Authorization: Bearer $TOKEN" \ "$RESOLVE_URL/api/instances/$INSTANCE_ID/resume"The platform calls resolver.run(config, session_factory, emitter, resume=True).
The resolver reads its checkpoint state and continues.
Graceful stop
Section titled “Graceful stop”POST /instances/{id}/stop sends a stop request to the running resolver:
- Platform calls
resolver.stop(reason: str)→ resolver should checkpoint and return - Resolver returns any
ResolverResult(typicallystatus="cancelled") - Platform sets final instance status accordingly
- If resolver doesn’t respond in time → platform force-terminates the container
# Request graceful stopcurl -X POST "$RESOLVE_URL/api/instances/$INSTANCE_ID/stop" \ -H "Authorization: Bearer $TOKEN" \ -H "Content-Type: application/json" \ -d '{"reason": "user requested stop"}'Duration expectations
Section titled “Duration expectations”| Spec complexity | Typical duration |
|---|---|
| Simple (1-2 endpoints) | 10–20 minutes |
| Medium (3-5 endpoints) | 20–35 minutes |
| Complex (6+ endpoints) | 35–60 minutes |
| Multi-feature projects | 1–3 hours |
Instances that escalate to awaiting_input pause until the user responds. Container
setup adds 15 seconds (cached) or ~5 minutes (cold) to the first run.
Monitoring lifecycle in practice
Section titled “Monitoring lifecycle in practice”Poll for terminal state
Section titled “Poll for terminal state”import time, requests
def wait_for_terminal(instance_id: str, base_url: str, token: str) -> dict: headers = {"Authorization": f"Bearer {token}"} TERMINAL = {"completed", "failed", "cancelled"}
while True: resp = requests.get(f"{base_url}/api/instances/{instance_id}", headers=headers) instance = resp.json() status = instance["status"]
if status in TERMINAL: return instance
if status == "awaiting_input": # Check for pending input requests reqs = requests.get( f"{base_url}/api/instances/{instance_id}/input-requests?status=pending", headers=headers ).json() if reqs: print(f"Waiting for input: {reqs[0]['prompt']}")
time.sleep(15)Watch via SSE
Section titled “Watch via SSE”# Stream all events until the instance terminatescurl -N "$RESOLVE_URL/api/instances/$INSTANCE_ID/events?token=$TOKEN" | while IFS= read -r line; do [[ "$line" != data:* ]] && continue EVENT="${line#data: }" TYPE=$(echo "$EVENT" | jq -r '.event_type // .type') echo "[$TYPE]" [[ "$TYPE" == "done" ]] && breakdoneRead status events from the stream
Section titled “Read status events from the stream”# Extract the lifecycle from eventsstatus_changes = [ {"from": e["data"]["from"], "to": e["data"]["to"], "at": e["timestamp"]} for e in events if e.get("event_type") == "resolve.instance.status_changed"]