People in the loop
A person watching a session can talk to the run inside it. They can send the agent a message, answer a question the agent asked, stop the agent, or hand the session back after taking it over. All of it goes through the run’s inbox, and every message, answer and action lands in the report.
This is for tasks that are mostly automatic but sometimes need a decision from a person, such as which of two matching records to pick, whether to accept a dialog, or a login the agent should not see. The run asks, waits for a bounded time, and carries on with the answer or without it.
The inbox, the sidebar and the control actions need no model. In a run with a goal:, the runtime’s own loop reads the messages, and that loop needs a model anyway. In mcp mode, your agent reads them.
The sidebar
Section titled “The sidebar”The sidebar sits beside the Kasm viewer. It is on when the session sets KASM_RUNTIME_SIDEBAR to the name of an installed sidebar; unset, the viewer has no sidebar. The bundled sidebar has three tabs:
- Home: the run and what the session can do.
- Timeline: each step as it happens, with its result and screenshots.
- Inbox: a compose box with Attach… and Send, a Stop button while the agent’s loop runs, an I’m done, finish button during a handoff, and any open question from the agent rendered as a form with an Answer button.
To send anything, the sidebar needs the messaging token, KASM_RUNTIME_INBOX_TOKEN. The bundled sidebar reads it from its page URL, ?kasm_runtime_token=<token>. Without it the sidebar can still show the run where the observe routes are open, but every write is refused. A replacement sidebar can do the same things through the same routes; see Replace the sidebar.
The inbox
Section titled “The inbox”The inbox exists when KASM_RUNTIME_INBOX_TOKEN or KASM_RUNTIME_SESSION_TOKEN is set. One consumer takes messages out of it.
- In a
runwith agoal:, the runtime’s own loop. - In
mcpmode, the outside caller: your agent or orchestrator reads messages fromGET /messages?since=<rev>or themessageevents on/events, reports what it did with them, and asks its own questions withPOST /requests.
A message’s state records what the runtime observed: pending (on disk), queued (taken by the consumer), dispatched (a model request carrying it was attempted), responded, and at the end undelivered or unknown. unknown means the runtime cannot tell whether the model saw the message, for example when a request timed out. The runtime never guesses that a message was seen.
In the runtime’s loop, messages are delivered just before the next model request. Each becomes a user turn prefixed [operator]. A message never interrupts a tool call already running; it waits as pending until the call ends. Messages that arrive after the loop has stopped are marked undelivered with the reason.
The limits are operator settings under evidence.
| Setting | Default | What it bounds |
|---|---|---|
inbox_message_chars |
4000 | one message’s text; a longer message is refused |
inbox_attachment_bytes |
8 MiB | one attachment |
inbox_context_chars |
16000 | operator text held in the model’s context at once; older answered messages are folded into a summary |
inbox_summary_chars |
2000 | that summary |
inbox_drain_max |
8 | messages delivered per model request |
Message text and answers are redacted with the same rules as the rest of the run before they are written anywhere. See Secrets and evidence.
Questions from the agent: ask_human
Section titled “Questions from the agent: ask_human”When the run has an inbox, the runtime’s loop offers its model an ask_human tool. The model gives a plain question, or a list of typed fields (string, integer, number, boolean, string[], with enum, minimum, maximum and similar constraints) when it needs a choice or a number.
The loop waits until a person answers or the deadline passes. The runtime validates the answer against the fields before accepting it, and refuses an invalid one with an error per field, leaving the question open. The model receives the answer as JSON. If nobody answers in time, the model gets an error and carries on with its own judgement. Asking does not end the run or fix the verdict.
| Operator setting | Default | Meaning |
|---|---|---|
runner.ask_human_timeout_s |
300 | longest wait for one question |
runner.ask_human_budget_s |
600 | total waiting for people in one run; 0 means nobody is attending, and ask_human returns at once |
Waiting for a person does not count against the budget for waiting on the application. Only the run’s deadline ends a run that is waiting. Every question, its answer and how long the person took are in report.json under inbox.requests.
The bundled sidebar renders string, integer, number and boolean fields. A question with other field types shows as “This request needs a richer UI”, with the raw request visible.
Stop and handoff
Section titled “Stop and handoff”Two control actions are open to the messaging token:
stopis a graceful stop. The runtime makes no further model request and starts no tool call from a response that has already come back. A tool call already running finishes. The loop then exits with the reasonstopped by operator, and the verdict follows the usual rules for a loop that ended without one. The sidebar shows “stopping…” until nothing is running. To end the process at once, use a signal or Kasm’s session controls instead.finish_handoffends a handoff early. When the runtime’s model callsescalate, the verdict is fixed astest-errorwith the outcomeescalated, the loop exits, and the runtime pauses so a person can take the session. The pause lasts until the run’s deadline, orrunner.escalation_pause_sif set. A person who has finished presses I’m done, finish, andreport.escalation.ended_byrecordsoperator.
A handoff does not resume the agent. After the pause the run finishes with the verdict fixed at the call.
Limits
Section titled “Limits”- The runtime cannot tell whether a person is present. A deployment that knows nobody is watching sets
runner.ask_human_budget_s: 0. - Of attachments sent to the loop, PNG and JPEG are described to the model by the vision model; GIF and WebP are listed but not described; other types are named as unsupported.
- How a sidebar gets the messaging token is up to the deployment: put it in the viewer URL, or have a backend hold the token and post on the person’s behalf.