# 1.8.8b5 — one conversation on two devices, and a sweep of the CLI

An opt-in preview. Stable remains 1.8.7.

## One conversation, open on a laptop and a phone at once

Opening a conversation on a second device used to fail with `Session is already
attached to another connection` (#1606). The refusal came from the relay, but
the rule behind it was in every layer: the protocol defined a session as a
connection, and the Host streamed a turn only to the socket that sent the
prompt. Now the session is the Host's, and every connection to it is a viewer:

- a turn started on one device streams to the owner's other devices, each
  reading it from its own position, with the prompt first as `user_message`;
- any device can answer an approval or stop the turn, whichever started it;
- through the relay, each client socket gets its own `conn_id` — the agent
  declares it understands that inside its signed ANNOUNCE — and the Host, not
  the relay, decides who may read what.

"The same person" means the same identity: import your recovery phrase on the
second device. A different identity naming the session id is given its own
session and sees nothing, as before (#696).

The refusal was also hiding a bug that lost work. When a device reconnected,
the Host kept whichever copy of the conversation had more LLM calls in its
*last turn* — a count that restarts every turn — so going back to the laptop
after a turn on the phone could erase the phone's turn. The merge now compares
turns first.

Verified live with `scripts/two_device_acceptance.py`, which starts a real Host
and drives three signed clients (two sharing an identity, one not) directly and
through the production relay; all eight properties pass on both paths.

This needs the relay from oo-api v0.1.19 (deployed) and, for the web client to
show the other device's question, `@connectonion/react` 0.4.4-rc.5.

## The CLI sweep

- **`co ai`** logs a refused command in full — the truncated call line had cut
  off `| head -40`, the part that was refused — and answers a call to a tool
  that does not exist with the tools that do, so the model stops retrying it
  (#1493, #1292). Includes a contribution from @521Peter (#1519).
- **`co browser close`** snapshots the daemon's process tree before closing,
  waits at most a minute, stops anything it left running — Chrome sits in its
  own process group, so a hung daemon used to orphan a paid, still-billing
  browser — and exits 1 naming it (#1496). `status` no longer calls an ended
  paid session open (#1457); an explicit `--no-headless` with no display is
  refused instead of silently launching HeadlessChrome (#1339); a cron job
  finds the daemon a login shell started (#1475); the busy-tab note says
  *this tab*, and how to open your own (#1605).
- **`co proxy diagnose`** lists every endpoint the relay announces for the host
  with how each answered, and says "usually a cloud firewall" when all time out
  (#1387). **`co deploy --to`** keeps a connectonion build the project's
  requirements.txt names and prints which version ended up on the server (#1375).
- **`co whatsapp react <id> <emoji>`** reacts to anyone's message (#1633), and
  **`co whatsapp group create` / `group add`** report every number's outcome —
  added, invite-only, no account, or not confirmed — instead of trusting a
  success WhatsApp gives even when someone was left out (#1617).

## Known limits

- When one device answers an approval, the other device's dialog stays open
  until the turn moves on; resolution is not broadcast yet.
- A native Codex/Claude Code Work Room turn does not fan out to other devices;
  ordinary agent turns do.
- `co whatsapp group` and `react` were tested against the SDK's types, not on a
  live account (creating a group adds real people). A listener started before
  this version must be restarted to handle them.
- On Windows, `co browser close` checks for leftover processes only when psutil
  is installed; otherwise it keeps its old behaviour.
- Left for a decision rather than guessed at: `co ai` slash commands (#965),
  which `co` commands an unattended run may call (#1293), a busy daemon's
  `status` (#1473), adopting an existing deployment (#1505), and scheduled
  Gmail sends (#934).

```sh
python -m pip install --upgrade --pre 'connectonion==1.8.8b5'
co --version
```
