> ## Documentation Index
> Fetch the complete documentation index at: https://docs.mobilerun.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Mobilerun Assistant

> Teach any AI agent to talk to the Mobilerun virtual assistant over chat — send tasks, stream replies, and handle human-in-the-loop question and approval cards.

The Mobilerun **Assistant skill** teaches an AI agent to hold a conversation with the Mobilerun virtual assistant (the VA) over the chat API. You talk to the VA the way you'd talk to a person: send it a task, read the streamed reply, and — this is the part that matters — respond when it pauses to ask a human a question or to get sign-off on a sensitive action (human-in-the-loop, or **HITL**).

This skill covers the chat endpoints under `/assistant/chat` and the `client.assistant.conversations.*` methods in the [TypeScript](https://www.npmjs.com/package/@mobilerun/sdk) and [Python](https://pypi.org/project/mobilerun-sdk/) SDKs.

<Note>
  This is the **chat** skill. To have an agent directly control a phone (tap, swipe, type), use the [OpenClaw · Android control](/skills/openclaw) skill instead.
</Note>

## Install

Download the pre-packaged skill and load it into any compatible agent runtime:

<Card title="mobilerun-assistant.skill" icon="download" href="https://github.com/droidrun/skills/releases/latest/download/mobilerun-assistant.skill">
  Download the latest release from GitHub
</Card>

The `.skill` file is a zip archive containing the `SKILL.md` knowledge pack. Refer to your agent runtime's documentation for how to load a `.skill` file. All the skill needs at runtime is a Mobilerun API key in `MOBILERUN_API_KEY` (`dr_sk_...` — create one on the [API Keys](/api-keys) page).

## How a conversation works

A conversation lives in a **session** (a persistent chat thread with a title). Each message you send starts a **turn**: the assistant streams back its reasoning, tool calls, and text until the turn settles.

<Steps>
  <Step title="Create a session">
    `POST /assistant/chat/sessions` with a `title`. Returns a session `id`.
  </Step>

  <Step title="Send a message and stream the reply">
    `POST /assistant/chat/message` with `{ sessionId, message }` and `Accept: text/event-stream`. Streaming is the only mode that works for long turns or HITL — the buffered JSON mode hard-times-out at 110s.
  </Step>

  <Step title="Read the stream until it settles">
    Handle text deltas and tool parts. The turn ends with a settle signal — `completed`, `error`, or an `aborted-*` reason.
  </Step>

  <Step title="Respond to any HITL card">
    If the assistant raises a question or approval card mid-turn, collect the user's decision and post it back (see below). The turn resumes once resolved.
  </Step>
</Steps>

## Human-in-the-loop (HITL)

Mid-turn, the assistant can pause and ask a human for input. This is the core of the skill — get it right and unattended integrations stay safe.

<CardGroup cols={2}>
  <Card title="Question card" icon="circle-question-mark">
    Stream part `tool-question`. The assistant is asking a clarifying question (e.g. "Which of these two restaurants did you mean?"). Answer with `POST /assistant/chat/question`, or dismiss it with `POST /assistant/chat/question/reject`.
  </Card>

  <Card title="Approval card" icon="shield-check">
    Stream part `tool-hitl-approval`. The assistant wants to perform a sensitive action (a payment, a deletion) and needs a human sign-off first. Respond with `POST /assistant/chat/permission` and `once`, `always`, or `reject`.
  </Card>
</CardGroup>

Both cards move through the same state machine: `input-available` while the card is open, then `output-available` (resolved) or `output-error` once a human responds.

<Warning>
  **Blocked is not broken.** When a card is `input-available`, the turn is paused waiting for a human — not failed. Keep the stream open, surface the card, collect an explicit decision, and post it back. Do **not** retry the send (that starts a second turn and returns `409`), do **not** inject an answer as a follow-up chat message, and do **not** auto-approve an approval card. Use `always` only when the user explicitly asks for durable approval; `reject` is the safe default when they decline.
</Warning>

### Answer a question

`POST /assistant/chat/question` with `{ questionId, answers }`. `answers` is an outer array aligned with the card's `input.questions`; each inner array is nonempty and holds `{label}`, `{custom}`, or `{label, custom}` selections. Include an `Idempotency-Key` header on retries.

```typescript theme={null}
await client.assistant.conversations.answerQuestion(
  { questionId, answers: [[{ label: "A" }]] },
  { headers: { "Idempotency-Key": key } },
);
```

### Answer an approval

`POST /assistant/chat/permission` with `{ permissionId, response }`, where `response` is exactly `once | always | reject`.

```typescript theme={null}
await client.assistant.conversations.answerPermission({
  permissionId,
  response: "once",
});
```

## Reconnecting and turn lifecycle

* **Reconnect after a drop:** `GET /assistant/chat/stream` replays the active turn from its start (a pending HITL card comes back on reconnect; `204` if nothing is running), then `GET /assistant/chat/messages` refetches full history — which also rehydrates any open card.
* **One turn per session:** sending while a turn is in flight returns `409`. Check `turnActive` from `GET /assistant/chat/messages` before sending.
* **Abort:** `POST /assistant/chat/abort` with `{ sessionId }` stops that session's in-flight turn (idempotent).
* **Stale session:** `404` (unknown/archived) or `410` `session_machine_replaced` (runtime recycled — start a fresh session). `402` means the account is out of credits.

## Reference

The downloadable `SKILL.md` carries the full endpoint table, curl / TypeScript / Python examples for every call, and the complete list of stream settle reasons and pitfalls. For request and response schemas, see the [TypeScript](https://www.npmjs.com/package/@mobilerun/sdk) and [Python](https://pypi.org/project/mobilerun-sdk/) SDK types.
