
# Errors, usage, and titles

The Pi session translates low-level provider events into state the widget can act on. It
keeps recoverable failures beside the composer, opens settings only when a choice there can
fix the turn, and reports the context and cost data attached to completed model messages.

## Provider errors

Agentak extracts useful messages from provider error bodies and replaces bare status lines
with actionable text. A network failure becomes a browser-oriented message instead of an
SDK exception.

These statuses open the settings page automatically:

| Status        | Why settings can help                                    |
| ------------- | -------------------------------------------------------- |
| `401` / `403` | The provider refused the API key or account permissions. |
| `402`         | The account is out of credit.                            |
| `404`         | The selected model is not offered.                       |

The error remains visible above the composer. Closing settings does not erase it.

Rate limits, timeouts, full context windows, server failures, and network errors stay on
the transcript because changing provider settings is not always the answer. The error row
can offer **Retry** and **Dismiss**.

## Retry behavior

Retry removes a failed empty assistant turn and continues from the user or tool result that
preceded it. It does not append a second copy of the failed turn.

A catalog failure is different: retry loads that provider's model catalog again. A turn
cannot retry while another turn is streaming.

Stopping aborts the active provider or local-model operation. Any tool confirmation still
waiting is denied with a stopped-run reason.

## Context and cost

After a completed assistant turn, the composer can show:

- context tokens used by the latest request
- the selected model's context window
- total input, output, reasoning, and cache tokens across turns
- accumulated input, output, cache, and total cost

On-device models report zero provider cost. Where a local API cannot provide exact tokens,
Agentak uses the best runtime count or estimate available.

The meter turns amber when Pi's compaction threshold says the remaining window is low.

Lower-level hosts can use `toContextUsage(messages, model)` to produce the same view.

## Compaction

The meter also carries the answer to a spent window. **Compact** asks the current model to
summarize the turns so far, then replaces them with that summary and the recent turns. The
conversation continues under the summary, and the transcript shows a checkpoint where the
replaced turns were.

- Compaction runs by itself where the alternative is a conversation that cannot go on:
  after a turn settles with the window nearly spent, and after a turn the provider refused
  because the request did not fit. A turn refused that way is run again once the summary
  has landed, so the message is answered rather than lost. Set `autoCompact: false` to
  leave the meter's button as the only way.
- One summary per answer. A summary that freed less than it hoped, and a retry that failed
  the same way again, both leave no answer since the last summary — and nothing writes a
  second one over it. Opening a stored conversation that was left at its limit costs no
  request either; the next message is what starts one.
- The button waits while every turn is one a compaction would keep. That is roughly the
  first 20k tokens on a large model, and a quarter of the window on a small one —
  summarizing there would replace nothing and shorten nothing, so the panel says so
  instead of running the request.
- A compaction leaves a window no provider has counted yet. The meter estimates it from
  the summary and the turns it kept until the next answer arrives with a real number, so
  compacting shows as the drop it is.
- The request runs outside the agent loop, so the transcript never carries the question.
- The cut never falls between a tool call and its result. A conversation short enough that
  the recent turns are the whole of it is left exactly as it was.
- A compaction that fails or is stopped leaves the conversation untouched. A failure shows
  in the error row with the retry button.
- Pi keeps 16k tokens for the summary and 20k tokens of recent turns. Both are capped
  against small windows: half the window for the summary, a quarter of it kept.

`session.compact()` starts one; `usage.compacting` reports one running, and
`usage.canCompact` reports whether one would change anything. Lower-level hosts
can call `compactMessages({ messages, model, apiKey })` and load what it returns.

## Titles

Without another model request, Agentak derives a title from the first user message and
limits it to a short header label.

Set `generateTitle` to ask the current model for a title after the first answer:

```ts
const session = createPiSession({ generateTitle: true });
```

Or declare it on the widget so it can change without replacing the session:

```tsx
<ChatPanel session={session} generateTitle />
```

Generation uses one extra request, asks for at most six words, and runs only after the
first answer is complete. It does not enter the transcript. If it fails, the first-message
title remains.

A generated title is included in `PiSnapshot` and built-in history. A derived title is
recreated from the stored transcript.

## Transcript conversion

`toViewMessages()` converts Pi messages into the widget parts:

- user and assistant text
- reasoning, including a note for provider-redacted thinking
- tool calls, approval state, results, failures, and returned images
- untrusted page-tool labels
- compaction checkpoints, from `compact()` or supplied by a host, and branch summaries

A provider error can exist on an assistant turn with no text. That turn still renders so
the person can see what failed.

Use `describeFailure(message)` and `failureStatus(message)` when a custom Pi surface needs
the same wording and status extraction.
