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

# Your UI

> The agent is an event stream your frontend renders. The protocol, the HTTP and SSE binding, and reconnecting without losing anything.

nativeharness ships no UI. It ships a **protocol**: a versioned stream of events that a
terminal, a web app, a test and a replay all consume identically. Rendering that stream is the
whole of a frontend, and the CLI's `chat` command is a two-hundred-line example of doing it.

<Note>
  Reference components — a message list, a tool card, an approval card, a composer — are on
  the roadmap, to be extracted from an example app rather than designed ahead of one.
</Note>

## The events

Every event is an envelope — `seq`, `ts`, `type`, `workspace_id`, `session_id`, `turn_id` —
around a typed payload. The ones a UI renders:

| Event                          | Render as                                                                  |
| ------------------------------ | -------------------------------------------------------------------------- |
| `session_created`              | a conversation exists — its actor, mode and title                          |
| `title`                        | the session was given (or gave itself) a title                             |
| `turn_started`                 | a new assistant message begins                                             |
| `token`                        | append text to it as it streams                                            |
| `progress`                     | a status line: `deciding`, `running`, `reviewing`, `compacting`, `waiting` |
| `tool_started`                 | a tool card — for `bash`, the command                                      |
| `output`                       | live output appended to the card, `stdout` or `stderr`                     |
| `tool_result`                  | the card's outcome: status, exit code, files changed                       |
| `approval_required`            | an approval card showing the **effects**, with allow / deny                |
| `approval_resolved`            | the card's outcome — who decided, and what                                 |
| `ask_user`                     | the questions, with their options                                          |
| `child_started` / `child_done` | a nested subagent                                                          |
| `compacted`                    | a small notice that history was folded                                     |
| `error`                        | inline, with whether it was recoverable                                    |
| `cancelled`                    | who stopped it                                                             |
| `done`                         | the turn's final status, stop reason, and usage totals                     |

`token` and `output` are the only events a live consumer gets that a replay might not — and a
replay carries their content inside `hop` and `tool_result` respectively, so nothing is lost
either way.

## Persist, then emit

An event reaches a subscriber only after it is in the workspace's log. This is one line of
code to get backwards, and everything about reconnecting rests on it: a client that saw an
event can always fetch it again, and one that missed some can ask for everything after the
last `seq` it has.

## The HTTP binding

`@nativeharness/protocol` provides `SessionService`, which drives turns server-side, and a
Node `http` handler over it:

```
POST /workspaces/{ws}/sessions              create a session
POST /workspaces/{ws}/sessions/{id}/turns   start a turn — returns at once
POST /workspaces/{ws}/sessions/{id}/resume  continue a suspended turn
POST /workspaces/{ws}/sessions/{id}/cancel  ask the in-flight turn to stop
GET  /workspaces/{ws}/sessions/{id}/stream  SSE; honours Last-Event-ID
GET  /workspaces/{ws}/sessions/{id}/events  the persisted log, as JSON
```

```ts theme={null}
import { createServer } from "node:http";
import { SessionService, createHandler } from "@nativeharness/protocol";

const service = new SessionService({ store, agent, events });
createServer(createHandler({
  service,
  // Resolve the caller from your own auth. Absent means every request is the owner —
  // acceptable only in local development.
  authorize: async (request, workspaceId) => sessionOwns(request, workspaceId),
})).listen(3000);
```

A turn is started with one request and watched with another — the stream. A browser that
loses its connection reconnects with `Last-Event-ID` and receives exactly the events it
missed. A turn that is still running when the page reloads is still running.

The handler is a regex and a switch on purpose: it is a reference binding. A deployment on
Express, Hono or anything else calls `SessionService` directly.

## From the browser

```ts theme={null}
const source = new EventSource(`/workspaces/${ws}/sessions/${session}/stream`);
source.onmessage = ({ data }) => render(JSON.parse(data));
```

Then `fetch` to `/turns` with the user's text, to `/resume` with an approval decision or
answers, and to `/cancel`.

## Model output is text

`token` text, `ask_user` questions and options, and hop output are **model output**. Render
them as text, never as markup. A model that writes `<script>` must produce a message that
says `<script>`.

The full contract — envelope, every payload, reconnect and replay, versioning — is
[`protocol.md`](/reference/specs).
