# view
What the DotBot panel says, apart from how it is drawn.
Every function here is pure: it takes readings from `modules.dotbot.source` and
returns the words and the state the panel renders.
| Function | Answers |
|----------|---------|
| `emptyState(availability)` | The title and detail shown when no DotBot surface is present. |
| `hintLine(agent, usage)` | The line under the composer naming what pays and what is left, or nil when there is nothing honest to say. |
| `usageNotice(agent, usage, models)` | The banner above the composer: a notice under 25% left, a warning under 10% offering a cheaper model only when that model is one the agent can run, and a used-up state naming the reset and that a message sent now is answered once it resets. |
| `contextGauge(answer, preview?)` | The context gauge of a conversation from the plugin's `context` answer: the fill (its context against its model's window), where the auto-compact handle stands and the range it can move in (the least a threshold may be, to the most it takes effect at, never past the window), `184K / 1M` or `184K / max unknown`, and `compacts at 600K`. `gaugeTokensAt(gauge, fraction)` is the threshold a press or a drag at a share of the bar sets (to 1,000 tokens); `gaugeKey(gauge, from, key, shift?)` is where an arrow (10,000, Shift 100,000), Page Up / Down, Home or End moves it. `gaugeCount(n)` writes a size the way the gauge does. |
| `continuationNotice(continuation)` | The banner while the conversation's turn waits for a usage window to reset: when it continues, with Continue now and Cancel. |
| `problemNotice(agent)` | The banner a failed sign-in puts above the composer. |
| `composerState(availability, agent, usage)` | Whether the field takes typing, what it says while empty, and whether it carries the model and thinking chips. A spent window keeps the field open and says which limit is used up and when it resets: the provider decides. |
| `connectSheet(agent, flow)` | The sheet a disconnected, expired or unlinked agent shows in place of a conversation. |
| `connectedNotice(agent, account)` | The line a freshly-connected agent shows above its first turn. |
| `metaLine(meta)` / `metaWho(meta)` | The per-turn meta line: model, thinking level, tokens, tool calls, duration — and who paid. |
| `pickerSubtitle(agent)` / `pickerAction(agent)` | What an agent's row in the picker says, and the word on its button when it needs one. |
| `rowKey(row)` / `mergeRows(history, live)` | A transcript row's identity (a tool row is its call, a thinking row or usage line is itself) and the merge of stored history with the live rows, which shows the overlap once. A live row the history does not hold (a turn's usage line is never stored) is drawn after the stored row the live row before it matched, so it stays with its own turn; live rows nothing of which is stored yet follow the history. A history list is keyed once, when it first reaches `mergeRows`, so a merge on every redraw costs the live rows. |
| `palette()` / `MONO` / `MESSAGE` | DotBot's own look: the editor's palette with DotBot's colours and corner radii laid over it, the font code and figures are set in, and the surfaces of a person's message and an opened thought. |
| `groupCarrierRows(rows, toolsRunIn)` | Runs of `tool_exec` calls collapsed into one row naming the tools they ran. |
| `thinkingLabel(row, now)` / `finishThinking(row, now)` / `foldThinking(rows, from)` / `thinkingRow(...)` / `toggleOpen(state, key)` | Thinking as a folded row: its header, its clock, the fold of one turn's phases into one row, and the open/closed state. |
| `splitMarkdown(md)` / `tableNode(...)` / `tableWeights(...)` / `codeNode(...)` / `markSelectable(node)` | An answer's tables and fenced code lifted out of the markdown and drawn so they wrap, and every piece of an answer marked selectable. |
| `imageNode(P, key, path, exists)` | A tool's picture, or the line saying it is no longer available. |
| `linkText(id, label, onClick)` | Text that looks like a link and acts like one, its callback keyed by `id`. |
| `agentStateWord(state)` / `agentActive(run)` | How an agent's state reads (`Queued`, `Starting`, `Working`, `Waiting`, `Done`, `Failed`, `Cancelled`), and whether it still works. |
| `agentElapsed(run, nowMs)` / `agentWaited(run, nowMs)` / `duration(seconds)` | How long an agent has worked, counting from its start while it works (none while queued), how long a queued agent has waited to start, and a span as `8s`, `1m 05s`, `1h 02m`. |
| `oneLine(text, limit)` | Text as one line, its line breaks made spaces, cut at `limit`. |
| `agentDoing(run, nowMs?)` | What an agent is doing now and what kind of line that is: the tool it is in, a phase, "Waiting for your answer: <tool>" while a call waits on the person, "Waiting for engine work" once its loop has ended and engine work it started has not, its answer once done, or why it failed. |
| `agentUsage(run)` | What an agent has used: tool calls, input, output and cache hit rate. |
| `agentsHeading(runs)` / `agentsHeadingParts(runs)` | The line over a conversation's agents, naming how many are in each state, e.g. `Agents · 1 queued · 2 working · 1 done`, and its pieces, each with the state it counts. |
| `agentsUsageLine(totals)` | The line under the composer naming what the agents used, from `subagentUsage`: how many, their input, output and cache hit rate, and the credits they were charged. |
| `USAGE_PENDING` | What `usageLine` says before a provider has reported a window. |
| `workflowStatus(run)` | How a workflow run's state reads and the state its hue comes from: `Working` while an agent holds one of its requests, `Waiting` while requests wait for one, `Running`, `Finished`, `Failed`, `Cancelled`. |
| `requestStateWord(state)` | How a workflow request's state reads (`Waiting`, `Claimed`, `Answered`, `Failed`, `Cancelled`). |
| `workflowElapsed(run, nowSec)` / `workflowProgress(run)` | How long a run has taken, and `{ answered, asked, failed, text }` with `text` such as `2 of 5 answered · 1 failed`. |
| `phaseName(name)` / `workflowPhaseLine(run, phases)` | A phase's name (`Start` for the unnamed one), and the phases as `Plan 1/1 · ▸Build 1/3`, the one the run is in marked. |
| `workflowStarter(run, title?, agentName?, address?)` | Who started a run: `in “<conversation>”` (or `in DotBot conversation <first 8 of its id>` when the list names it by its id alone), `by agent <name> in ...`, or `by <caller>` from what the engine recorded. |
| `requestTiming(req, nowSec, agent?)` | A request's line: the agent working it or the runner holding its claim, how long it has been claimed or waited or took, the claims that ran out and its corrections. |
| `answerText(answer, limit?)` | An answer as text: a string as it is, a table of named string, number or boolean fields as one `name: value` line each, anything else as indented JSON, cut at `limit`. |
| `workflowsRunningText(count)` | `1 workflow running`, `3 workflows running`. |
| `waitingOn(runs, args)` | The Working row while `wait_for_agents` runs: the agents it waits on, by name, newest first as the cards stand, and whether it waits for the first of them; once they have all ended, that they have. |
The thresholds the usage ladder turns on are `noticeThreshold` (25) and
`warnThreshold` (10).
# dotbot
DotBot, the agent in Zero's editor: the component that runs a person's own
Claude or ChatGPT plan (or OrigoZero credits) from inside Zero, and the
DotBot panel in the editor's right dock that talks to it. This world is
where the plugin's published copy lives; the engine's built-in library pins
a version of it.
## What is here
| Part | What it is |
|------|------------|
| `plugin.wasm` | The DotBot component (`wasm32-wasip2`), built in the DotBot repo from `crates/dbot-zero-plugin`. |
| `plugin.yaml` | The plugin's grants: its scope, the hosts it may reach, its private storage. |
| `surface.module` | The plugin's Luau surface, what `require("@builtin::plugins.dotbot")` resolves to. |
| `source.module` | What the panel reads: agents, accounts, conversations, history, turns, agents a turn started. |
| `service.module` | The trusted service between the panel and the component: calls, the event ring, jobs. |
| `panel.editorPanel` | The DotBot panel itself: toolbar, transcript, agents, composer, settings. |
| `view.module` | What the panel says and how it looks (`palette()`), apart from how it is drawn. |
| `transcript.module` | The transcript and the agents section, drawn as virtual rows. |
| `workflows.module` | The Workflows view. |
Each module's own README says what it does in detail.
## Seeing a panel change
The editor draws its DotBot tab from the built-in library, not from this
world, so an edit to the panel here does not show in the real tab. Open the
world's own panel code over a fake plugin with example data instead:
```bash
luau --lang luau 'require("/source/dotbotPanelPreview.module").open()'
```
It opens a **DotBot preview** tab beside the real one. Run it again after
each edit. `dotbotPanelPreview.module/README.md` says what it shows.
## Shipping a change
Work on a branch of this world and open a pull request into `main`. How a
merged change reaches the engine (publishing `plugin.wasm`, and the engine's
pin of this world) is written down in the DotBot repo, in
`docs/zero-plugin.md`.