# Dots and your dev host

Hand work to the supervisor agent on your own development machine from Dots, ChatGPT or Claude, get a task id at once, and hear back when a worker asks a question, finishes or fails. Your answers go to the exact worker that asked, and 0bridge tells you whether it took them. Nothing goes through Slack.

If the machine already keeps a work ledger that a team of agents works from (your desk and Telegram put your words there), 0bridge can put what you tell Dots there the same way instead: see [Ledger mode](#ledger-mode).

## What it connects

On the machine, an OpenClaw agent (the supervisor, for example one named `lead`) runs coding agents in Herdr panes, and `host-task` keeps the record of each task: its questions, answers and result. 0bridge doesn't replace any of them. `0b agent`, the same small program as [Your agents from your phone](/docs/remote-control), connects that machine to your account and does all the work there:

- A request from your AI app becomes a `host-task` task (`T-012`), and the supervisor gets a message about it with the task id, through `openclaw agent`, in a session of its own for that task. A second request, from another conversation, gets its own task and session, so it doesn't disturb the first.
- When a worker asks something, `host-task` records it as a question. `0b agent` reads new `host-task` events every few seconds and sends questions, completions and failures to 0bridge, once each, even across restarts.
- Your answer is recorded with `host-task answer` and typed into that worker's pane, and 0bridge waits to see the worker pick it up. A question the worker (or the supervisor) wrote down itself goes back to the supervisor with your answer instead.

The supervisor is set up on the machine, never from 0bridge's side: 0bridge only knows that the machine has one, its agent's name and a label.

The supervisor decides where and how the work runs. The repos and modes you allow with `0b agent allow` limit the agents 0bridge starts itself ([Your agents from your phone](/docs/remote-control)); they don't limit host work, and a request's repo is passed to the supervisor as you said it. Give the supervisor its own rules for what it may touch.

## Set it up

1. On the dashboard, under Settings → AI apps, check that **Agent control** is on. New accounts start with it on; an account from before 2026-10-06 turns it on there, which needs you to confirm it is you.
2. On the machine, if `0b agent` isn't running yet:

```
0b agent on
```

3. Point it at the supervisor:

```
0b agent supervisor openclaw --agent lead --label dev-herdr-agent
```

It checks that `host-task`, `openclaw` and `herdr` answer (`--host-task`, `--openclaw` and `--herdr` take other paths), and warns if OpenClaw doesn't list that agent. It saves each program's full path, found on your shell's PATH as you run it, so the background service finds them even when its own PATH doesn't have `~/.local/bin`. `0b agent supervisor status` shows the setup, how far it has read and the last error; `0b agent supervisor off` disconnects it. The first time, it starts at the end of the `host-task` log, so old events aren't sent; questions already open still show up when you ask for them.

## What you can ask

From any AI app with 0bridge and agent control on, eight tools:

| Tool | What it does |
|---|---|
| `bridge__host_request` | Hands a piece of work to the supervisor and returns the task id at once. The work goes on in the background. |
| `bridge__host_status` | One task with what happened lately (questions, answers, the supervisor's replies, the result), or the recent tasks. |
| `bridge__host_followup` | More instructions for a task, passed to the supervisor in that task's session. |
| `bridge__host_questions` | The questions workers are waiting on you for, with their ids. |
| `bridge__host_answer` | Your answer to one question, by its id. |
| `bridge__host_updates` | New questions, completions and failures since the last call. This is how Claude hears back. |
| `bridge__host_receipts` | What this app sent to the host lately, and where each send stands. See [Check that a send arrived](#check-that-a-send-arrived). |
| `bridge__host_delivered` | Ledger mode: records that you were shown the team's messages, a delivery receipt for each. See [Ledger mode](#ledger-mode). |

Try: "Ask my dev host to add a test for the date parser in the 0bridge repo", then "How is T-012 doing?"

The same request from the same app within 10 minutes is answered with the task already made, so a retried call doesn't start the work twice; the same follow-up to the same task isn't sent twice either. A request, follow-up or answer sent with a `key` is recorded once however often it's sent again ([Check that a send arrived](#check-that-a-send-arrived)). Each machine numbers its own tasks and questions, so with more than one machine an id the two share needs the machine's name too, and 0bridge only sends a follow-up or an answer to the machine where it saw that task or question. When the machine is offline, status comes from what 0bridge saw last, and says so.

## Answers go to the worker that asked

`bridge__host_answer` takes a question's id (from the event, or `bridge__host_questions`). Your AI app should ask you first and pass on your words.

A question comes one of two ways, and the event's `source` says which. `herdr`: the worker stopped at a prompt in its pane (Herdr shows it blocked), and the answer is typed there. `worker`: the worker or the supervisor recorded the question with `host-task emit`; nothing waits at a prompt for it, so 0bridge passes your answer to the supervisor with the question's id and the task, and the tool says forwarded. `host-task` takes such a question off the task whenever the worker isn't blocked, so 0bridge keeps it open itself until it's answered, the worker asks a newer one, or the task finishes.

For a question Herdr raised:

1. The answer is stored in `host-task`, for that question only, once. A question that's no longer the task's current one is refused, with the current one's id.
2. 0bridge checks that the worker's pane is still the one that asked (the same pane and the same worker name in Herdr), and that you aren't looking at that pane yourself. If anything doesn't match, nothing is typed anywhere: the supervisor gets the answer with the question's id instead, and the tool says it was forwarded.
3. The answer is typed into that pane only, and 0bridge watches Herdr until the worker leaves the waiting state. Then it records `answer_delivered` in `host-task` and the tool says delivered, with the state before and after (`blocked → working`).

If the worker doesn't move within a short wait, the answer is not typed a second time: the tool says pending, and if it still hasn't moved after two minutes, `answer_unconfirmed` is recorded and the supervisor is asked to look. Answering the same question again returns the first result and types nothing.

A pane that is focused on the machine is never typed into: the supervisor gets the answer, and the tool says the pane is focused.

Your answer is typed as keys, then Enter, at whatever prompt the worker is blocked on: when that's a coding agent asking permission to run a tool, it answers that prompt, and can approve the tool call, outside 0bridge's approval rules for the agents it starts.

## How you hear back

### Dots and ChatGPT: events

ChatGPT supports [MCP events](https://developers.openai.com/plugins/build/mcp-events) in Work chats on the web, in the desktop app with Cloud selected, and in dots. 0bridge offers three:

| Event | When | Filters |
|---|---|---|
| `host.task.question` | A worker asks a question. Carries the task, the question's id and its text. | `machine`, `project`, `task` |
| `host.task.completed` | The supervisor marks a task completed, with evidence. A worker saying it's done isn't a completion. | `machine`, `project`, `task` |
| `host.task.failed` | A task is marked failed. | `machine`, `project`, `task` |

After setting up, open the 0bridge plugin's page in ChatGPT and rescan it: the events show next to the tools. Then ask, in a Work chat or a dot: "Watch my host tasks: tell me each question with its task and question id, and each completion or failure." ChatGPT subscribes when you ask; 0bridge doesn't subscribe for it.

0bridge checks the address ChatGPT gives (public https only, no private networks, no redirects), sends it a signed verification first, and then one signed POST per event ([Standard Webhooks](https://www.standardwebhooks.com/)). A delivery that fails is retried a few times over about 20 minutes with the same event id. Subscriptions survive restarts on both sides. A subscription with an expiry that has only failed, without a break of an hour, for a day stops until ChatGPT renews it; one without expiry keeps trying and is removed after a week of that. Disconnecting the app in the dashboard, the app revoking its tokens, deleting the token it uses, or turning agent control off stops deliveries at once. Each subscription shows in the activity log (`events.subscribe`), with the callback's host only.

ChatGPT's own rule applies: events missed while something was down aren't replayed. `bridge__host_status` and `bridge__host_questions` always show where things stand.

Your [webhooks' events](/docs/triggers) are offered too, as `webhook.received`, if you turned on **Webhook events in chat apps**.

### Claude: pull and notifications

Claude's connectors have no events, so Claude asks: `bridge__host_updates` returns what's new since its last call, and with `wait` it waits up to 20 seconds. Try: "Any questions from my dev host?"

To be told without asking, set an ntfy topic or email under Settings on the dashboard, as for [your agents](/docs/remote-control). A question is sent every time; a completion or failure isn't sent when another one for that task, from up to 30 seconds earlier, already was. Each is sent at most once: one that ntfy didn't answer in time isn't sent again, since it may have arrived. A notification says which task and what happened ("asks a question", "completed", "failed"), never the question's text, and links to the dashboard.

### Other apps

Any MCP client gets the tools. One that supports MCP events (protocol `2026-07-28`) can subscribe the way ChatGPT does. Instinct has no MCP and no terminal, so it can't connect at all: [Instinct](/docs/clients/instinct).

## Ledger mode

Some hosts don't hand work to one supervisor agent: the work ledger (`host-task`) is where everything starts, and a team of agents there picks it up. Your desk and Telegram write your words into it (`dev_request` for new work, `user_followup` and `user_decision` for an existing task), and the team's supervisor turns a request into a task, asks you what only you can decide, and reports back. In ledger mode, what you tell Dots, ChatGPT or Claude enters that ledger the same way, and they read the team's questions and results back to you.

On the machine, with agent control on:

```
0b agent supervisor ledger --label devlead
```

Only `host-task` is needed (`--host-task <path>` if it isn't on your PATH; its full path is saved). `0b agent supervisor status` shows `kind: ledger`, how far it has read, and the requests still waiting for a task id.

What happens:

- **A request** is written as a `dev_request`: your words exactly, then a line saying where they came from, like `출처: ChatGPT via 0bridge · 2026-10-06 17:31 KST · 요청 hr_k3f9x2a1`. 0bridge makes no task, runs no agent and types into no pane. The tool answers with the request's id (`hr_…`) and its place in the ledger, not a task id: the team gives it one when it takes it up. Asking again with the same words within 10 minutes, or 0bridge retrying, writes nothing twice.
- **Its task id** comes when the team makes the task and keeps the request's id in what it writes (the request's source line, the task's contract). 0bridge sees the id in the ledger and links the two; from then on `bridge__host_status request=hr_…` shows that task, and Dots can follow it by its id.
- **A follow-up** (`bridge__host_followup`) on a task is written as `user_followup` on it. Before the request has a task id, pass the request's id as the task: it's written without a task, starting `(요청 hr_… 후속)`, and the team sees which request it's about.
- **The team's questions**: when the team asks you something with options (a `[선택지]` block, the same one Telegram shows as buttons), it arrives as `host.task.question` with those options, and in `bridge__host_questions` and `bridge__host_updates`. Your answer (`bridge__host_answer`, with `choice` for the option you picked) is written as `user_decision` on that task, with the option the way a Telegram button gives it (`choice: T-044|e1145|A / 선택: A) …`). A question closes when the team asks a newer question on that task, when you've decided on another channel (the team clears it once your answer is in), or when the task ends — not when the team sends other messages on the task; an answer to a closed one is still recorded as your decision on that question, noting that it had closed, so the team sees it and nothing you said is lost. Questions a worker asks the team itself stay with the team.
- **Progress**: the team's other messages to you (results, follow-ups, replies), on any task, show in `bridge__host_status` and as `progress` in `bridge__host_updates`.
- **Notices**: the host's own notices to you, most with no task (server alerts, usage summaries, schedule briefings and reminders, a team message with no task), come as `notice` in `bridge__host_updates`, each with its `kind`, so your app can decide what to tell you. With Dots as your one channel, it sees everything Telegram used to.
- **Delivery receipts**: once your app has shown you the team's messages or questions, it calls `bridge__ledger_append` with action `delivered` (or `bridge__host_delivered`) with their ids (a progress or notice item's `event`, a question's id), and 0bridge records a delivery receipt for each in the ledger (`host-task delivered`, `primary_delivered`), so the team can tell from the ledger that you saw them. The channel is `dots` (the ledger knows Dots and Telegram; any chat app through 0bridge counts as Dots), and the receipt names the app, the time and the call (`ChatGPT via 0bridge · 2026-10-13 17:31 KST · hd_…`), plus the app's own link for the conversation if it gives one. Only ids 0bridge has as the team's messages from that machine are sent, so only recent ones take a receipt (0bridge keeps the last 5,000 or so events per machine); a receipt that's already there comes back as such, so it needs no key and asking again is safe. Receipts aren't listed in `bridge__host_receipts` (that's what the app sent; these are in the ledger). With more than one ledger machine, one call per machine, with `machine`. Nothing else happens: no message is sent anywhere, no question is answered or closed. The machine needs `0b` 0.2.40 or later; an older one is asked to update.
- **Completion and failure** come back as before (`host.task.completed`, `host.task.failed`).
- **Notifications**: the host already tells you on its own channel, so 0bridge sends no ntfy or email for a ledger host.
- **Outside context** (a Trello comment for a task) is kept in the ledger as `context_received` on the task, and isn't passed to the task's worker: that takes the OpenClaw supervisor. Its status says so.

## Check that a send arrived

An app can lose a call: ChatGPT can cancel one before it reaches 0bridge (its confirmation or its automatic review stopped it), and then nothing was sent at all, even if the chat moved on. So an app shouldn't tell you "sent" until it has seen 0bridge's answer, and when it isn't sure, it can check and send again without doubling anything.

**A key for each send.** `bridge__host_request`, `bridge__host_followup` and `bridge__host_answer` take an optional `key`: the app's own id for that send, such as the id of your chat message (up to 100 letters, digits and `. _ : @ / + = -`). The same app sending the same thing again with the same key, with the same tool, gets the first result back, marked as a repeat, and nothing new is recorded: not on 0bridge, not in the ledger. "The same thing" is the words (spacing and case aside) and the task, question and option. A key that comes back with something else (another conversation's made-up `msg_1`, say) is refused with why, and nothing is sent: it never returns the old send's result in place of the new one. So use a new key for each send, and the same key only to retry it. A key holds for a day, also after 0bridge or the machine restarts; after that it's free again. If the first try never got a reply from the machine, the retry goes with the same request id, and the machine records it once. A send the machine refused (an answer for the wrong task, say) leaves its key free for the corrected one. Without a key, everything works as before.

**Receipts.** `bridge__host_receipts` lists what this app (or this token) sent lately, newest first: each request (`hr_…`), follow-up (`hf_…`) and answer, with its time, its task or request, its key, and where it stands:

| State | Means |
|---|---|
| no reply yet | 0bridge has it, the machine hasn't confirmed: send it again with the same key |
| in the work ledger | the machine recorded it (with its host event) |
| taken up | a request the team made a task for (with the task id) |
| with the supervisor, delivered, pending, refused | on an OpenClaw machine, as `bridge__host_status` says |

It never shows the words you sent; a task's title is the host's. `since` takes minutes (`30`, `30m`, `2h`, `1d`) or an ISO time, 24 hours by default, 7 days at most; `limit` up to 100. A send that isn't listed didn't reach 0bridge.

So, for an app: send with a key; if the call errors or you're not sure it went, call `bridge__host_receipts` (or just send again with the same key); tell the user it was sent only once it's listed as in the work ledger (or later).

**`bridge__ledger_append`.** In ChatGPT and Dots, 0bridge lists four tools ([code mode](/docs/clients/chatgpt#never-refresh-again)), and the host tools ran through `bridge__execute_write`, which is marked as able to delete and to reach outside, because some tools behind it can. When your account has agent control on and one of your machines keeps a work ledger (`0b agent supervisor ledger`), apps get a fifth tool for exactly this:

```
bridge__ledger_append
  action    request | followup | answer | delivered
  text      your words
  task      followup: T-012 or hr_…; answer: the question's task
  question  answer: the question's id
  choice    answer: the option's letter
  events    delivered: the ids of the team's messages and questions you were shown
  ref       delivered: the app's link for the conversation (optional; no key needed)
  project, priority (request), machine, key
```

It runs `bridge__host_request`, `bridge__host_followup`, `bridge__host_answer` or `bridge__host_delivered` with the same checks (agent control, the token's scope, the answer's task check, the activity log), and goes only to a machine whose supervisor is the work ledger, where it only adds an entry: it's marked as not read-only, not destructive and not reaching outside (and not idempotent: a retry is safe only with the same key). A machine on the OpenClaw supervisor refuses it and says so; use `bridge__execute_write` there. Search and describe point those four host tools at it, with the action to use. `bridge__execute_write` still runs them as before.

It's listed only for such an account, so nobody else's list changes. If 0bridge can't read your machines within a moment when the list is asked for, it lists the tool anyway for an account with agent control on, so the list doesn't change with a passing hiccup; a call to it still goes only to a ledger machine. After turning agent control on or setting up the ledger, refresh 0bridge in the app once to see it (the four tools need no refresh; this one does, once).

From a terminal: `0b host receipts --since 30m`, and `--key <id>` on `0b host request`, `followup` and `answer`.

## From a terminal (Dots VM, any machine)

A dot has a computer of its own with a terminal, and its copy of 0bridge's tools in ChatGPT can be out of date. The `0b` CLI calls the same host tools by name, so from that terminal (or any machine you're signed in on) you can hand work to your dev machine's work ledger without the chat app's tool list.

On the dot's computer:

```
npm install -g 0bridge
0b setup --agent-vm --name dot --no-vault
```

It prints a link; approve it once on your phone. That signs the computer in as an [agent computer](/docs/agent-vms): its token expires after 7 days, and you can revoke it any time on the dashboard ([Revoke](/docs/agent-vms#revoke)). `--no-vault` leaves your vault's secrets off that computer.

Then:

```
0b host request "결제 페이지 모바일에서 깨지는 것 고쳐줘" --project 0bridge
0b host status hr_k3f9x2a1
0b host followup hr_k3f9x2a1 "특히 iOS Safari"
0b host questions
0b host answer 602 --task T-060 "A로 가자" --choice A
```

`0b host request` prints the request's id (`hr_…`) on its first line, or the task id once the team has one. Text can come from stdin with `-` (`cat note.md | 0b host request -`). Each command takes `--json` for scripts and exits non-zero when 0bridge or the host refuses, with the reason printed as it was given.

For notifications, run `0b host updates` on a schedule and keep its cursor: the last line it prints is the next cursor.

```
cursor=$(cat ~/.0b-host-cursor 2>/dev/null)
0b host updates ${cursor:+--cursor "$cursor"} --wait 20 > /tmp/host-updates.txt && tail -n 1 /tmp/host-updates.txt > ~/.0b-host-cursor
```

`--kinds question,completed,failed,progress,notice` narrows what comes back.

What an agent computer may do here: only the host commands above (and `0b host receipts`), and only for a machine whose supervisor is the work ledger (`0b agent supervisor ledger`). There every write is an entry in the ledger (`dev_request`, `user_followup`, `user_decision`); 0bridge types nothing into any pane. A machine on the OpenClaw supervisor, where an answer is typed into a worker's terminal, refuses an agent computer and says so; use a chat app or the dashboard for it. An agent computer still can't list your machines, start or steer coding agents, or subscribe to host events (it pulls them with `0b host updates`). The gateway checks the supervisor when each call is made, on the machine the call goes to, and that machine's `0b agent` checks its own setup again before it takes the call, so switching it to OpenClaw takes effect at once. Nor can an agent computer list your machines or their tasks through 0bridge's API.

## What keeps it safe

- Off until the machine is pointed at a supervisor, and whenever agent control is off (any connected AI turns it off when you ask, with `bridge__settings_off`).
- The supervisor's messages are sent without any delivery option: OpenClaw doesn't post its reply to Slack or anywhere else. 0bridge itself only sends to the AI app that subscribed and to your own ntfy topic and email.
- Answers are typed only into the pane that asked, checked again right before typing, and never twice.
- Host text (questions, replies, evidence) is masked for secrets on the machine and again on the server, and your AI app is told it's outside data, not instructions.
- Requests, follow-ups and answers are in the audit log with the task and machine, never the text.
- At most two supervisor messages run at once on the machine, and messages for one task go in order.

## What 0bridge can read

Your requests and answers, and the host's questions, the supervisor's replies and results, pass through 0bridge and are kept with your machine so status works while it's offline. They are masked, not sealed: treat them like the task pages in [Your agents from your phone](/docs/remote-control).

## What's checked and what isn't

As of 2026-10-05, this has been tested on a local copy of 0bridge with stand-ins for `host-task`, OpenClaw, Herdr and ChatGPT's receiver, not yet with a real ChatGPT account, a real dot, or the owner's machine. We'll date each one here when it's seen working. Until then, keep watching the host the way you do today.

Ledger mode (2026-10-06) has been tested the same way, with stand-ins for `host-task` and ChatGPT's receiver, not yet on the real machine. So has `0b host` from a terminal: against a stand-in gateway and the machine hub's tests, not yet from a real dot. Keys, receipts and `bridge__ledger_append` (2026-10-07) too: whether ChatGPT stops cancelling the ledger tool's calls is still to be seen in ChatGPT itself. Delivery receipts (2026-10-14) against the stand-in `host-task` and the machine hub's tests, not yet on the real machine or in Dots.

Known limits:

- One supervisor per machine: OpenClaw or the work ledger.
- In ledger mode a request gets its task id only if the team keeps the request's id (`hr_…`) in what it writes when it makes the task; until then it shows as waiting.
- When several machines have a supervisor, say which one (`machine`).
- The callback's address is checked before every delivery, but between that check and the connection a name could point somewhere else; 0bridge's servers can't reach your private network either way.
