Files
LLeMbas-CLI/harness/progress.md
T
HomerandClaude Opus 5.5 4b8786b7f6
ci / check (push) Waiting to run
Harness spec 3.4.0: progress phases, and subagents v2 — the main agent and its tree
progress.md: thirteen fixed phases (queued … failed), each { phase, since, data },
their texts as shared i18n keys (loop.json progress), one line every client draws,
sent beside the old status text in /api/v2 and over the link (capability progress).

subagents.md §5–§13 (supersedes team.md where they differ): only the main agent
adds, assigns, replaces, stops and decides; sub-managers direct those placed under
them; ids, positions, parents; Min above 1 opens slots; permissions inherited and
never widened, request options decided by the main agent, what nobody in the tree
can allow goes to the person; team_finish, findings, running out of context;
transcripts read-only; the tree over the link (capability tree). New tools
team_whoami, team_finish, team_findings, team_request, team_decide, team_assign,
team_replace. Conformance: progress, delegation; team updated.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-10 08:10:36 +00:00

108 lines
7.1 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Progress
Spec 3.4.0. **One way to say what a session is doing**, the same in every client — the web UI,
Desktop, the CLI, Android — and for every agent in it, the main agent and each subagent alike.
Before 3.4.0 each client invented its own subset from a free `status` text; that text stays, for
compatibility, and beside it goes a structured **progress** object with one of a fixed set of
**phases**. The texts are `loop.json` `progress`; the cases `conformance/progress.json`.
## 1. The object
```json
{ "phase": "running_tool", "since": 1762790400000, "tool": "bash", "what": "bun test" }
```
- **`phase`** — one of the thirteen names below, never another. A session doing nothing has no
progress: `null`.
- **`since`** — when this phase began, Unix time in **milliseconds** (an integer). A change of the
phase's data (tokens read, the rate) keeps `since`; a new phase, or the same phase for another
thing (another tool call, another model), starts a new one.
- The phase's **data**, as listed: every field named here, with these names and these types. A
field that is not known is left out (never `null`, never a guess), and the text says less.
## 2. The phases
In the order a turn usually meets them. `{{x}}` in a text is the field `x`; the texts are
`loop.json` `progress.texts`, word for word, and they are the shared i18n keys
`progress.<phase>` (and `progress.<phase>.<variant>`, `progress.reason.<reason>`,
`progress.what.<what>`) in every client's translations.
| Phase | Data | Text (`progress.texts`) |
|---|---|---|
| `queued` | `ahead?: int` — turns before it | `Queued` · with `ahead`: `Queued: {{ahead}} ahead` |
| `waiting_for_model` | `model: string` (its display name, else its id); `reason: "busy" \| "slots" \| "group"` | `Waiting for {{model}}: {{reason}}` |
| `loading_model` | `model: string` | `Loading {{model}}` |
| `reading_prompt` | `done: int`, `total: int` — prompt tokens processed of all | `Reading the prompt: {{done}} / {{total}} tokens ({{percent}}%)` · without `total`: `Reading the prompt` |
| `thinking` | — | `Thinking` |
| `writing` | `rate?: number` — tokens a second, as measured over this reply | `Writing: {{rate}} tok/s` · without `rate`: `Writing` |
| `running_tool` | `tool: string` (the tool's name); `what?: string` — the call's short title (the command, the path, the query), one line, at most 80 characters | `Running {{tool}}: {{what}}` · without `what`: `Running {{tool}}` |
| `waiting_for_person` | `what: "approval" \| "question" \| "plan"`; `tool?: string` (approval: the tool asked about) | `Waiting for you: {{what}}` |
| `waiting_for_subagents` | `running: int` — subagents working or waiting now | `Waiting for subagents: {{running}} running` |
| `compacting` | — | `Compacting the conversation` |
| `retrying` | `reason: string` — the server's error, one line; `in: int` — seconds until the retry, counted down by the client from `since`; `attempt?: int` | `Retrying in {{in}} s: {{reason}}` |
| `stopped` | `reason?: "person" \| "steps" \| "budget"` | `Stopped` · with `reason`: `Stopped: {{reason}}` |
| `failed` | `reason: string` — the error, one line | `Failed: {{reason}}` |
The fixed words a field stands for:
- `reason` of `waiting_for_model` (`progress.reason`): `busy` — *it serves one request at a time
and is answering another*; `slots` — *all its slots are in use*; `group` — *its server holds
another model now*.
- `what` of `waiting_for_person` (`progress.what`): `approval` — *allow {{tool}}?* (*an approval*
without `tool`); `question` — *a question*; `plan` — *the plan*.
- `reason` of `stopped` (`progress.stopped_reason`): `person` — *by you*; `steps` — *step limit*;
`budget` — *budget*.
- `percent` is `floor(done × 100 / total)`; numbers are written as integers, `rate` with one
decimal (`12.5`), in the client's own number format.
## 3. The line a client draws
**`{{text}} · {{elapsed}}`** (`progress.line`) for every phase but `stopped` and `failed`, which
draw their text alone. `elapsed` is now − `since`, written in whole seconds as a ticking clock writes durations
(`5s`, `1m 05s`, `1h 02m`), and kept moving by the client at least once a second while the
phase lasts. `retrying`'s `in` is likewise counted down from `since` (never below 0).
Every client draws the same line for the same object — the same words, from the same keys — and
its own look around it (a spinner, a colour, a bar for `reading_prompt`). Where a line has no room
(a phone's notification, a tree row) the text alone, without `· elapsed`, is enough; nothing else
is shortened.
## 4. When each phase holds
What decides the phase, so that two implementations agree on the same moment:
- `queued` — a prompt waits behind a turn that runs (the device's turn queue; the server's per
chat). `waiting_for_model` — the turn may start but its model may not take it yet: a one-request
server answering another request (`busy`), every slot of the model held (`slots`; `team.md` §8),
another model of its one-model server in use (`group`). `loading_model` — only where the
implementation is told the model is being loaded (the server sees it; a device that cannot tell
skips it).
- `reading_prompt` — the request is sent and no output has come; with `done`/`total` from the
server's prompt-progress frames (`quirks.json` `prompt-progress`), without them where none come.
- `thinking` — reasoning streams; `writing` — reply text (or a tool call's arguments) streams.
- `running_tool` — from a tool call's start to its end; with calls in parallel, the one that
started first. `waiting_for_person` — an approval card, a question or a plan to approve is open
for this agent. `waiting_for_subagents` — the agent's turn waits on subagents: a `task` call in
the foreground, or the main agent with nothing to do but its subagents' reports (`subagents.md`
§9).
- `compacting` — a compaction runs. `retrying` — a failed request will be sent again after a wait.
- `stopped` / `failed` — the turn ended other than by finishing: stopped by the person, a step limit
or a budget; failed with an error. They are the last progress of that turn and stay until the
next one begins; a turn that finished normally ends with `null`.
## 5. Where it is sent
- **`/api/v2` (LLeMbas)**: every frame of a chat's stream that carries the `status` text carries
`progress` beside it — the object, or `null` — and a frame is sent when the progress changes:
a new phase at once, a data-only change (`done`, `rate`) at most four times a second. `status`
stays as it was, its text unchanged, for clients that do not read `progress`.
- **The link (`acp.md`, capability `progress`)**: the `phase` event `{ progress, agent? }` —
`agent` the subagent's id (`subagents.md` §5) when the progress is a subagent's, absent for the
main agent's — under the same pacing; `progress` in `_lembas/session/status`, so a client
reattaching draws it at once. The device sends it; the server passes it on in `/api/v2` as its
own.
- **The subagent tree** (`subagents.md` §12): every agent carries its current `progress`.
A client that reads `progress` draws it and ignores `status`; one that does not, draws `status` as
before.