# Asking a human

# Asking a human

Consumer-facing guide to `ask_a_human` — the structured human-in-the-loop question an agent raises when it is blocked on **human judgment**: a preference, a confirmation only a person knows, a pick among options. For the delegation model the escalation path rides on, see [Delegating to sub-agents](delegate.md).

## What an ask is (and is not)

`ask_a_human` puts exactly **one** structured question to the human and pauses **only the asking run** until it is answered. It is not a free-roaming chat pause: agents are taught to exhaust their tools first — anything a data query, knowledge-base search, or context read answers is never a valid ask.

Two shapes only:

| Shape             | Agent supplies                    | Human answers with                                                       |
| ----------------- | --------------------------------- | ------------------------------------------------------------------------ |
| `multiple_choice` | `question` + 2-6 `options` labels | `{ "kind": "multiple_choice", "option_id": "...", "other_text": "..." }` |
| `free_form`       | `question` only                   | `{ "kind": "free_form", "text": "..." }`                                 |

**The Other write-in is guaranteed.** On every multiple-choice ask the platform injects exactly one `{ "id": "other", "label": "Other" }` option (deduping any caller-supplied "Other"). Picking `other` requires a non-empty `other_text` write-in — an empty or whitespace-only write-in is rejected at resume validation.

When the run resumes, the agent receives the answer as the tool result: `{ "answer": "<chosen label, write-in, or free text>", "option_id": "<id, multiple_choice only>" }`.

## Who pauses (escalation semantics)

Only the **asking** run ever blocks:

- **Direct ask** — the asking run transitions to `awaiting_input` with `pending_input_type: "ask_human"`. Any sub-agents it dispatched keep running to completion.
- **Sub-agent ask** — the sub pauses itself and the question is escalated to its caller's event stream as a `child_needs_human` frame (`{ child_run_id, question, timestamp }`). Sibling sub-agents keep running; the caller is never paused by the escalation. The answer resumes the **waiting sub directly** — resume targets the paused child's run id.

Multiple concurrent asks on one conversation are surfaced oldest-first (FIFO); each is answered independently against its own run.

## Answering

An ask is answered through the existing resume surface, targeted at the paused run:

```bash
curl -X POST https://api.amdahl.com/api/platform/v1/agents/<run_id>/resume \
  -H "Authorization: Bearer $API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "input": { "kind": "multiple_choice", "option_id": "other", "other_text": "SMB first, actually" } }'
```

The body must match the run's persisted `pending_input_schema` (read it off the paused run row); a payload that violates the contract — unknown `option_id`, empty Other write-in, empty free text — is rejected with a validation error and the run stays paused for a corrected answer.

## Behavior per surface

An ask behaves differently depending on how the run was started, controlled by the run's `on_question` mode:

| Surface / mode                                               | What happens on an ask                                                                                                                                             |
| ------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| Interactive (console, `on_question: "pause"`, the default)   | The run pauses; the question renders as a pause widget; answer via resume.                                                                                         |
| REST                                                         | Reading the run shows `status: "awaiting_input"` + the question in `pending_input_context`; answer via the same resume endpoint.                                   |
| MCP                                                          | The paused state is visible on run reads; the connected model answers via the resume tool or relays the question to its own user.                                  |
| `on_question: "auto"` (bare API callers running on judgment) | No pause. The tool returns `{ "skipped": true, "reason": "on_question_auto" }` and the agent proceeds on its own judgment, stating the assumption it proceeded on. |
| Routines / headless (`on_question: "none"`)                  | No pause, no hang. The tool returns `{ "error": "no_human_in_loop" }` immediately — scheduled runs must never depend on human asks.                                |

## Limits

- One pending ask per run; a second ask in the same turn is refused with `ask_in_flight`.
- Multiple-choice asks take 2-6 caller options (the write-in is added on top); a question whose only option would be Other is rejected — use `free_form`.
- Free-form answers and Other write-ins are capped at 4000 characters.
- An answer inbox for headless runs (email notification, TTL, `on_no_answer` policies) is a planned follow-up and **not** part of v1 — headless asks fail fast by design.
