diff --git a/openhands/usage/cloud/cloud-api.mdx b/openhands/usage/cloud/cloud-api.mdx index c8e7bb6d..6229e336 100644 --- a/openhands/usage/cloud/cloud-api.mdx +++ b/openhands/usage/cloud/cloud-api.mdx @@ -127,6 +127,7 @@ The `status` field indicates the current state of the conversation startup proce - `WORKING` - Initial processing - `WAITING_FOR_SANDBOX` - Waiting for sandbox to be ready - `PREPARING_REPOSITORY` - Cloning and setting up the repository +- `SETTING_UP_SKILLS` - Configuring agent skills and tools - `READY` - Conversation is ready to use - `ERROR` - An error occurred during startup @@ -167,6 +168,272 @@ The endpoint streams a JSON array incrementally. Each element represents a statu Each update is streamed as it occurs, allowing you to provide real-time feedback to users about the conversation startup progress. +### Checking Conversation Status + +After starting a conversation, you can check its status to monitor whether the agent has completed its task. + + + The examples below show basic polling patterns. For production use, add proper error handling, + exponential backoff, and handle network failures gracefully. + + +#### Step 1: Check Start Task Status + +When you start a conversation, you receive a start task ID. Poll this endpoint until `status` becomes `READY` and `app_conversation_id` is available: + +```bash +curl -X GET "https://app.all-hands.dev/api/v1/app-conversations/start-tasks?ids=TASK_ID" \ + -H "Authorization: Bearer YOUR_API_KEY" +``` + +**Response:** +```json +{ + "id": "550e8400-e29b-41d4-a716-446655440000", + "status": "READY", + "app_conversation_id": "660e8400-e29b-41d4-a716-446655440001", + "sandbox_id": "sandbox-abc123" +} +``` + +#### Step 2: Check Conversation Execution Status + +Once you have the `app_conversation_id`, check whether the agent has finished its task: + + + + ```bash + curl -X GET "https://app.all-hands.dev/api/v1/app-conversations?ids=CONVERSATION_ID" \ + -H "Authorization: Bearer YOUR_API_KEY" + ``` + + + ```python + import requests + + api_key = "YOUR_API_KEY" + conversation_id = "YOUR_CONVERSATION_ID" + + headers = { + "Authorization": f"Bearer {api_key}", + "Content-Type": "application/json" + } + + response = requests.get( + "https://app.all-hands.dev/api/v1/app-conversations", + headers=headers, + params={"ids": conversation_id} + ) + response.raise_for_status() # Raise exception for HTTP errors + conversations = response.json() + + if conversations: + conv = conversations[0] + print(f"Sandbox Status: {conv.get('sandbox_status')}") + print(f"Execution Status: {conv.get('execution_status')}") + else: + print("Conversation not found") + ``` + + + +**Response:** +```json +[ + { + "id": "660e8400-e29b-41d4-a716-446655440001", + "sandbox_status": "RUNNING", + "execution_status": "finished", + "selected_repository": "yourusername/your-repo", + "title": "Fix README" + } +] +``` + +#### Status Fields + +**`sandbox_status`** - The state of the sandbox environment: +- `STARTING` - Sandbox is being created. **Action:** Continue polling. +- `RUNNING` - Sandbox is active. **Action:** Check `execution_status` for task progress. +- `PAUSED` - Sandbox is paused (due to rate limits or user action). **Action:** The sandbox will resume automatically when resources are available, or resume manually via the UI. +- `ERROR` - Sandbox encountered an error. **Action:** This is a terminal state. Check conversation details in the UI for error information. +- `MISSING` - Sandbox was deleted. **Action:** This is a terminal state. Start a new conversation if needed. + +**`execution_status`** - The state of the agent's task (available when sandbox is `RUNNING`): +- `idle` - Agent is ready to receive tasks. **Action:** Continue polling if task was recently submitted. +- `running` - Agent is actively working. **Action:** Continue polling. +- `paused` - Execution is paused. **Action:** Continue polling; will resume automatically. +- `waiting_for_confirmation` - Agent is waiting for user confirmation. **Action:** This is a blocking state. The agent needs user input via the UI to proceed. Your polling loop should treat this as a terminal state or alert the user. +- `finished` - Agent has completed the task. **Action:** Terminal state. Task is done successfully. +- `error` - Agent encountered an error. **Action:** Terminal state. Check conversation in UI for error details. +- `stuck` - Agent is stuck and unable to proceed. **Action:** Terminal state. Manual intervention may be required. + + + **Terminal states** that should exit your polling loop: `finished`, `error`, `stuck`, `waiting_for_confirmation`. + The `waiting_for_confirmation` state requires user action through the UI before the agent can continue. + + +#### Complete Polling Example + +Here's a complete example that starts a conversation and polls until completion: + +```python +import requests +import time + +api_key = "YOUR_API_KEY" +base_url = "https://app.all-hands.dev" + +headers = { + "Authorization": f"Bearer {api_key}", + "Content-Type": "application/json" +} + +# Start a conversation +print("Starting conversation...") +start_response = requests.post( + f"{base_url}/api/v1/app-conversations", + headers=headers, + json={ + "initial_message": { + "content": [{"type": "text", "text": "Your task here"}] + }, + "selected_repository": "yourusername/your-repo" + } +) +start_response.raise_for_status() +start_task = start_response.json() +task_id = start_task["id"] +print(f"Start task ID: {task_id}") + +# Poll start task until conversation is ready (with timeout) +conversation_id = None +max_attempts = 60 # 5 minutes with 5-second intervals +attempts = 0 +while not conversation_id and attempts < max_attempts: + task_response = requests.get( + f"{base_url}/api/v1/app-conversations/start-tasks", + headers=headers, + params={"ids": task_id} + ) + task_response.raise_for_status() + tasks = task_response.json() + + if tasks and tasks[0].get("status") == "READY": + conversation_id = tasks[0].get("app_conversation_id") + print(f"Conversation ready: {base_url}/conversations/{conversation_id}") + elif tasks and tasks[0].get("status") == "ERROR": + print(f"Start task failed: {tasks[0].get('error', 'Unknown error')}") + exit(1) + else: + status = tasks[0].get("status") if tasks else "no response" + print(f"Start task status: {status}") + time.sleep(5) + attempts += 1 + +if not conversation_id: + print("Timeout waiting for conversation to start") + exit(1) + +# Poll conversation until agent finishes (with timeout) +# Terminal states: finished, error, stuck, waiting_for_confirmation +max_attempts = 120 # 1 hour with 30-second intervals +attempts = 0 +while attempts < max_attempts: + conv_response = requests.get( + f"{base_url}/api/v1/app-conversations", + headers=headers, + params={"ids": conversation_id} + ) + conv_response.raise_for_status() + conversations = conv_response.json() + + if not conversations: + print("Warning: Conversation not found") + time.sleep(30) + attempts += 1 + continue + + conv = conversations[0] + sandbox_status = conv.get("sandbox_status") + exec_status = conv.get("execution_status") + + # Check sandbox health first + if sandbox_status in ["ERROR", "MISSING"]: + print(f"Sandbox failed with status: {sandbox_status}") + exit(1) + + print(f"Execution status: {exec_status}") + + # Check for terminal states + if exec_status in ["finished", "error", "stuck"]: + print(f"Conversation completed with status: {exec_status}") + break + elif exec_status == "waiting_for_confirmation": + print("Agent is waiting for user confirmation in the UI") + print(f"Visit: {base_url}/conversations/{conversation_id}") + break + + time.sleep(30) + attempts += 1 +else: + print("Timeout waiting for conversation to complete") + exit(1) +``` + +### Listing All Conversations + +To list all your conversations, use the search endpoint: + + + + ```bash + curl -X GET "https://app.all-hands.dev/api/v1/app-conversations/search?limit=20" \ + -H "Authorization: Bearer YOUR_API_KEY" + ``` + + + ```python + import requests + + api_key = "YOUR_API_KEY" + headers = {"Authorization": f"Bearer {api_key}"} + + response = requests.get( + "https://app.all-hands.dev/api/v1/app-conversations/search", + headers=headers, + params={"limit": 20} + ) + response.raise_for_status() + result = response.json() + + for conv in result.get("items", []): + print(f"ID: {conv['id']}, Status: {conv.get('execution_status')}") + ``` + + + +**Response:** +```json +{ + "items": [ + { + "id": "660e8400-e29b-41d4-a716-446655440001", + "sandbox_status": "RUNNING", + "execution_status": "finished", + "selected_repository": "yourusername/your-repo", + "title": "Fix README" + } + ], + "next_page_id": null +} +``` + + + The search endpoint returns conversations in the `items` array. Use `next_page_id` + for pagination if you have more conversations than the `limit`. + + ## Rate Limits If you have too many conversations running at once, older conversations will be paused to limit the number of concurrent conversations.