Skip to content
Draft, pre-release documentation

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 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 exists when KASM_RUNTIME_INBOX_TOKEN or KASM_RUNTIME_SESSION_TOKEN is set. One consumer takes messages out of it.

  • In a run with a goal:, the runtime’s own loop.
  • In mcp mode, the outside caller: your agent or orchestrator reads messages from GET /messages?since=<rev> or the message events on /events, reports what it did with them, and asks its own questions with POST /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.

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.

Two control actions are open to the messaging token:

  • stop is 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 reason stopped 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_handoff ends a handoff early. When the runtime’s model calls escalate, the verdict is fixed as test-error with the outcome escalated, the loop exits, and the runtime pauses so a person can take the session. The pause lasts until the run’s deadline, or runner.escalation_pause_s if set. A person who has finished presses I’m done, finish, and report.escalation.ended_by records operator.

A handoff does not resume the agent. After the pause the run finishes with the verdict fixed at the call.

  • 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.