Questions
A question lets a running Turn ask a person for input and wait for the answer. Each question belongs to one Session and one Turn, has its own ID, declares the shape of the answer it accepts and records who answered.
import type { Turn } from "@helmr/sdk"
export async function requestApproval(turn: Turn) {
const { answer, answeredBy } = await turn.ask({
prompt: [{ type: "text", text: "Publish revision 42?" }],
type: "choice",
options: [
{ id: "approve", label: "Approve", value: true },
{ id: "decline", label: "Decline", value: false },
],
})
return { approved: answer.selected[0]?.value === true, by: answeredBy }
}
turn.ask resolves with { answer, answeredBy }. answeredBy is
{ kind: "user" | "api_key", id }. Validate the answer before acting on it;
answering a question does not finish the Turn.
Question types
The prompt accepts a string or Content with text and json parts. The question’s
type declares what a valid answer looks like.
| Control | Answer |
|---|---|
{ type: "text" } |
A string. |
{ type: "choice", options, multiple?, allowCustom?, allowNotes? } |
{ selected: [{ id, value } ], custom?, notes? } |
multiple: true allows more than one selection, and allowCustom: true allows
a custom alternative to a single selection, or custom text alongside multiple
selections. allowNotes permits supplementary notes; notes alone are not an
answer. Answers that do not match the question are rejected. Option fields, validation rules and size limits are
in the Questions reference.
Who can answer
A person answers from the CLI or from Slack; code answers through the client or API.
- CLI.
helmr session turn ask list,ask getandask answerfind a question by its Session and Turn, show its complete question, and submit an answer. See Answer questions. - Slack. For a Session routed to Slack, the question appears in the thread with direct buttons for short choices or an Answer button that opens a form. The person answering must have linked their Slack identity to Helmr. Each Slack question links to authenticated Console inspection and CLI instructions.
- Client or API.
turn.asks.answeron a Turn reference fromHelmrClient.
Any active member of the Organization can answer. An API key can answer only if it was issued with permission to respond to questions. Seeing a question, in Slack or elsewhere, does not by itself grant the right to answer it. The answer is attributed to the user or API key that submitted it.
Questions can be asked only from a root Session, not from a Session spawned as another Session’s child. See Delegation.
Lifecycle
| Status | Meaning |
|---|---|
pending |
Asked and waiting for an answer. |
answered |
Answered; the answer and responder are recorded. |
cancelled |
Withdrawn. It can no longer be answered. |
A question is answered at most once. It is withdrawn when:
- the
signalpassed toturn.ask(question, { signal })aborts; - the Turn ends for any reason, including interruption,
maxTurnDuration, cancellation or failure.
An answer is accepted only while its Turn is running. An answer to a withdrawn question is rejected; it never carries over to a later Turn.
Retries
Every answer carries a answer ID chosen by the answerer. If a response might not have arrived, retry with the same answer ID and the same answer; Helmr returns the original result instead of recording a second answer. A different answer ID, or the same ID with a different answer, is rejected once the question has been answered.
Rules for Agent code
- Await every question you ask. A Turn whose work finishes while a question it asked is still unresolved fails.
- New questions can be asked only while the handler is processing. A question awaited by a message callback that started before the handler returned can still be answered while the Turn finalizes.
maxTurnDurationincludes time spent waiting for answers. Without it, a Turn can wait for a person indefinitely.- Use a question, not a message, when you need an attributable answer. A message never answers a question.
- When a harness asks for permission or input through its own callback, bind the answer to that exact native request and propagate cancellation to it.
- Keep credentials out of prompts and answers.