# Tonbo Yield

`yield` captures the sessions of your coding agents (Claude Code and Codex) on every machine you use and lists them from any of them. It signs in, runs a capture service on each machine, snapshots the git work trees the agents work in, lists sessions, recalls the turns that changed a file, and hands a session off as a link that you, on another computer or harness, or a teammate takes.

Install it on macOS (arm64, x64) or Linux with glibc (x64, arm64) with one line, without sudo and without Node.js:

```console
curl -fsSL https://yield.tonbo.io/install.sh | sh
```

The installer, [`install.sh`](install.sh), uses the system `node` when it is 22 or newer and has npm beside it; otherwise it downloads the latest official Node.js 22 build from nodejs.org into `~/.tonbo/runtime/node-<version>`, checked against the release's `SHASUMS256.txt`. It installs `@tonbo/yield` into the private prefix `~/.tonbo/yield`, never a global npm, and writes the launcher `~/.local/bin/yield`, which runs it with that Node.js; when `~/.local/bin` is not on `PATH` it prints the line to add for your shell. On a terminal, a computer with no login yet then runs `yield login`, which signs in and starts capture, so the one line takes you from nothing to captured sessions; without a terminal, or with `YIELD_NO_LOGIN=1`, it prints that step instead. Running it again upgrades in place, and when the capture service is installed it reinstalls the service on the new version (which restarts it) and removes Node.js runtimes it no longer uses. `YIELD_VERSION` pins a version or dist-tag: `curl -fsSL https://yield.tonbo.io/install.sh | YIELD_VERSION=0.2.0 sh`. musl distributions (Alpine), 32-bit systems and Windows are refused with a message; on Windows, install the npm package instead. User Center serves the file at `https://yield.tonbo.io/install.sh` as `text/plain` (that host answers 404 for every other path until the Yield landing page exists) from an embedded copy (`apps/user-center/src/app/install.sh`); after changing `install.sh`, run `pnpm --dir apps/user-center generate:install-script`, or the route's test fails.

Or install the public package with Node.js 22 or newer:

```console
npm install --global @tonbo/yield
```

Merging a change to the `version` in `package.json` to `main` releases it: [`tonbo-yield-release.yml`](../../.github/workflows/tonbo-yield-release.yml) tests the package, publishes it through npm trusted publishing and installs it back from the registry, as the [Tonbo CLI release](../../docs/operations/tonbo-cli-release.md) does.

## Sign in

```console
yield login [--account <name>] [--name <name>] [--no-daemon]
yield status [--json]
yield whoami [--json]
yield logout
```

A login to your personal account also registers this computer and installs and starts the capture service, as `yield daemon install` does (`--no-daemon` only signs in); when the service manager refuses, it says why and the login stands. `status` is the one health view: the login and whom it is for, whether `api.tonbo.dev` answers, whether the capture service runs and, on Linux, survives logout, each agent's transcripts, version and last captured activity (and sessions changed in the last day that are not captured yet), snapshots waiting to be pushed, errors of the last 15 minutes, and this version against npm's latest. Each line that is not fine names the command that fixes it, and the command exits 1 when a line fails. `--json` prints `ok`, the `checks` (`name`, `level` of `ok`, `warn`, `fail` or `none`, `detail`, `fix`) and the facts behind them.

Errors say what happened and what to run, without a stack trace: an unreachable network, a refused login (`yield login`), a server error (with its request id) and a service manager that refuses (after `su`, in a container without systemd, or over SSH to a Mac without a desktop login) each get their own words. `YIELD_DEBUG=1` prints the stack too.

`yield` and the [`tonbo`](../tonbo) CLI share one login through the internal package [`@tonbo/cli-core`](../../packages/tonbo-cli-core): the same browser handoff, the same keys and the same credentials file, `${XDG_CONFIG_HOME:-$HOME/.config}/tonbo/credentials.json`. The file holds one CLI login key per account of this computer ([`credentials-v1.json`](../../contracts/cli/credentials-v1.json) `accounts`): your personal account's, named `CLI on <hostname>`, and one for each organization you connect a folder in, named `Yield CLI · <8 hex>` from a suffix the server derives from this computer's login secret, so the organization's admins never see your hostname. Logging in with either CLI signs both in, and a login replaces this computer's earlier key in that account, so the two never hold different keys. Capture, recall and registration always use the personal account's key, whatever account the last login chose; `logout` revokes every key of the file. `whoami` prints the account of the last login, your role and this machine's device id.

## Projects

```console
yield connect [folder] [--name <name> | --project <id>] [--account <name> [--yes]] [--json]
yield disconnect [folder]
```

`connect` binds a folder of this computer to a Project ([ADR-0016](../../apps/user-center/docs/decisions/adr-0016-org-git-workspaces.md) W-18) through `POST /v1/projects/connect`. Without a folder it binds the root of the git work tree you are in, or else the current directory. A git work tree joins the Project of its origin, made when the account has none, so every clone of one origin, on any computer, shares it; a work tree without an origin gets a Project named after its folder. A folder outside git is connected only by naming a new Project (`--name`) or joining one (`--project`). Without `--account` the Project is in your personal account; `--account <name>` binds it to that organization's shared team Project, which needs this computer registered (`yield login`), and logs this computer in to the organization first when it has no login there, without changing the account `tonbo` acts in. Before it binds, it says what every member sees of each session started in the folder from then on, its title, state and the files each turn writes, and that its prompts, replies and tool output stay with you unless you hand it to a teammate ([ADR-0017](../../apps/user-center/docs/decisions/adr-0017-projects-forks-visibility.md) D-12 and its metadata-only amendment, M-7), and asks; answering no keeps the folder personal and connects nothing. `--yes` answers for you, and without a terminal nothing is shared unless it is given. A folder connected in another account moves: it is disconnected there first. The folder is named only by a random id kept in `${XDG_STATE_HOME:-$HOME/.local/state}/tonbo-yield/folders.json`; its path never leaves the computer. `disconnect` removes the folder's binding (`DELETE /v1/projects/bindings/<id>`) and leaves the Project. `yield ps` and `yield daemon status` name the current folder's Project. A folder that is not connected stays in your personal account, as before.

## Pause and exclude

```console
yield pause [--for <duration>]
yield resume
yield exclude [folder]
yield include <folder>
```

`pause` stops capture and snapshots on this computer until `resume`, or for a while (`--for 30m`, `2h`, `1d`). Lines written while paused are never captured, also after capture resumes: the daemon drops each line whose time falls in a pause, and a line without a time of its own goes with the line before it. A paused turn makes no turn end, so no snapshot either, and `yield give` refuses while paused. `exclude` names a folder whose sessions are never captured on this computer; without a folder it lists them. A session whose cwd is in an excluded folder sends nothing, not even its start, and stays uncaptured after `include` names the folder again; only sessions started afterwards are captured. What was captured before a pause or an exclusion stays as it is. Both live in `capture-filter.json` in the state directory and never leave the computer; `yield status` shows a pause and the excluded folders.

## The device id

The device id names this computer for its whole life ([ADR-0015](../../apps/user-center/docs/decisions/adr-0015-byoc-machines.md) Q1). The first command that needs it makes a random UUID and keeps it in `${XDG_STATE_HOME:-$HOME/.local/state}/tonbo-yield/device.json` beside a SHA-256 of the operating system's machine id (macOS `IOPlatformUUID`, Linux `/etc/machine-id`). A login, a re-login or a revoked key leaves it unchanged. When the saved hash differs from the computer's, the state directory came from another computer (a cloned disk) and a new UUID replaces it; when the machine id cannot be read, the saved UUID stays. A lost state directory makes a new device, as a new computer would. Captured records (`device`), the Streams producer (`yield/<device id>`) and the snapshot refs of personal Projects (`refs/tonbo/u/<sub>/<device id>/<folder binding id>`) name it; nothing published to an organization does.

`yield login` and each start of the daemon register this computer as a device of your personal account (`PUT /v1/machines/self`, a Machine of kind `byoc`, ADR-0015) with the saved login: its device id, hostname, operating system, architecture, Yield's version and the harnesses it has. Only the key a CLI login issued registers, and only in your personal account; `--name` chooses the device's name the first time (`<os>-<n>` otherwise). The running daemon registers again every 300 seconds, give or take 30 (machine-v2 `kinds.byoc.heartbeat`, an interim until the state work of Yield M4), which keeps `last_seen` fresh; an unchanged report writes only `last_seen`, and a throttled one (429 `device_report_throttled`) counts as done. Registration never holds back capture: a refusal or a failure is printed or logged (once, until it changes) and tried again at the next login or heartbeat. A refusal no retry changes, such as a login to an organization (`self_requires_personal_account`), a key that a CLI login did not issue, the device limit or a taken name, stops the heartbeat until the saved login changes.

```console
yield devices [--json]
```

`devices` lists your computers, only the `byoc` Machines of your personal account (`GET /v1/machines`, every page, with the saved login; never `TONBO_API_KEY`), and under an organization's login says to log in to your personal account instead: the name, online when the last report is within machine-v2's `online_window_seconds` (900), when it was last seen, the OS and architecture, the agents it captures, Yield's version, and which one is this computer. A region whose devices could not be read is named below the table.

Yield 0.1.0 named the device by the key id of its login. Its state upgrades in place: each file keeps its offset, so nothing captured is sent again, and moves to the producer `yield/<device id>` from epoch 0, seq 0, saved before it is used. A batch saved under the old producer is first finished under it, so a resend after a crash is still answered as a duplicate, and the inbox moves once no such batch is left. Snapshots start a new chain on the new device's ref; the old refs stay.

## Capture

```console
yield daemon install [--since <date>]
yield daemon status [--json]
yield daemon uninstall
yield daemon run [--since <date>]
```

`install` writes a user service that runs `yield daemon run` with the current Node.js and starts it: a launchd agent at `~/Library/LaunchAgents/dev.tonbo.yield.plist` on macOS (`RunAtLoad`, `KeepAlive`) or a systemd user unit at `~/.config/systemd/user/tonbo-yield.service` on Linux (`Restart=always`). The service logs to `${XDG_STATE_HOME:-$HOME/.local/state}/tonbo-yield/daemon.log`. `uninstall` stops and removes it and keeps the capture state. `run` captures in the foreground and logs to stderr. `status` reports whether the capture runs, its last upload, how many sessions it tracks and skipped, the sessions it blocked and its recent errors. A systemd user service stops when the user's last login session ends, so on Linux `install` turns lingering on (`loginctl enable-linger`, without prompting) and, when the system does not allow that, prints the `sudo loginctl enable-linger <user>` to run; `status` reports whether the capture survives logout (`linger` in `--json`, null off Linux).

The capture follows [`contracts/sessions/capture-v1.json`](../../contracts/sessions/capture-v1.json), which the build embeds (`pnpm generate:contracts`):

- It discovers the account's store once an hour through `GET /v1/sessions/store` and again whenever the store refuses a request. An account without a store (409 `sessions_store_unavailable`: no active git workspace Agent) is reported by `daemon status` and `sessions` as needing an account owner to enable git, and the daemon asks again every 10 minutes.
- Streams does not accept the personal key itself: the client exchanges it at `POST <console origin>/api/iam/token` for a ten-minute Streams access token (`@tonbo/cli-core` `AccessTokens`), keeps it in memory, replaces it a minute before it expires, and exchanges again once after a Streams 401. A refused exchange asks for `yield login`; a 429 or 5xx is retried with backoff while the offsets stay.
- It watches `~/.claude/projects` and `~/.codex/sessions` and rescans them every two seconds; a missing root is fine. Each file is read from its saved byte offset, and only complete lines count. Each pass takes the most recently written files first and sends at most 8 batches of each, so a long backfill never holds back a live session, new files or state reports; the next pass starts at once while lines are left.
- Each line, except the contract's excluded types, becomes one record of the stream `session.<session id>`. Consecutive lines go in one append request (at most the contract's `max_batch_lines` and `max_batch_bytes`), which ends early at a line that causes an inbox record. Each request carries `Producer-Id: yield/<device id>`, `Producer-Epoch` and `Producer-Seq`, the request's ordinal within the epoch; the batch's seq and byte range are saved before it is sent, so a resend after a crash is the identical batch and is answered as a duplicate. The record carries the line as written, without redaction; a line that is not JSON is uploaded as it is too.
- When a session is first seen it appends `session_start` to `proc.inbox`, and at each turn end `turn_end` with the cwd, git root, branch, the paths written since the previous turn end (at most 50, relative to the git root inside it) and `stream_offset`, the session stream's `Stream-Next-Offset` after the turn's last record (the append's answer, or the stream's tail when a resent batch is answered as a duplicate).
- On every pass it decides each live session's state, `running`, `waiting`, `idle` or `ended` (capture-v1 `sources.<source>.state`; [Yield session state](../../docs/design/yield-session-state.md)), and appends `session_state` to `proc.inbox` when the state or the turn changes, and while a turn runs, when it has written new paths, at most every 10 seconds. Claude Code's live-process registry (`~/.claude/sessions/<pid>.json`) gives a Claude session's liveness and status; a running `codex` process whose working directory is the session's cwd keeps a Codex session live; otherwise a session's turn decides `running` or `idle`, and a session whose liveness cannot be told ends after 30 quiet minutes. A Claude session missing from the registry counts as ended only once its file has been quiet for a minute, since `claude -p` writes its transcript before its registry entry and removes the entry as it exits. A session that ends with its turn open (`claude -p` writes no `turn_duration`) has that turn's `turn_end` appended with its written paths. A session first seen already ended is not reported, but its open turn is still closed.
- The state, `${XDG_STATE_HOME:-$HOME/.local/state}/tonbo-yield/state.json`, keeps each file's offset with its producer, sequence and epoch, and is replaced atomically. The source files are the offline queue: while the store is unreachable the offset stays and the upload is retried with backoff, honoring `Retry-After`. A new epoch starts from seq 0, saved before it is used, when Streams has forgotten the producer (idle for 7 days) or is ahead of a lost local state.
- The first run also captures the session files changed in the last 14 days (`--since` sets the bound); older files are recorded as skipped until they change again.

### Snapshots

After each `turn_end` of a turn that ended in the last 10 minutes, in a cwd inside a git work tree, the daemon snapshots the folder the cwd is bound to (its Project's, see Routing) as [`contracts/sessions/recall-v1.json`](../../contracts/sessions/recall-v1.json) `snapshots` defines; the build embeds that contract too. Snapshots run one at a time beside the capture and never hold it up; a failure is logged as a warning, and a 4xx from the git token exchange or gitd pauses publishing for 10 minutes.

- The tree is the bound folder's, the whole work tree when the folder is its root and the folder's subtree alone otherwise, as `git add -A` sees it through a private index under `${XDG_STATE_HOME:-$HOME/.local/state}/tonbo-yield/snapshots/index/`, so `.gitignore` applies and the user's index, HEAD, branches and config stay untouched. Every git command runs the user's `git` with `GIT_TERMINAL_PROMPT=0`, `core.hooksPath=/dev/null`, no automatic gc and no credential helper. Files over 100 MiB, gitd's object limit, are left out with a logged notice.
- An unchanged tree makes no snapshot. Otherwise `git commit-tree` writes a commit whose parent is the previous snapshot of this device's chain (none for the first, the baseline) with the trailers `Tonbo-Session`, `Tonbo-Turn`, `Tonbo-Base` (the merge-base of HEAD and origin, preferred over an upstream on another remote; absent when nothing is published, so a never-pushed branch restores on another device from the whole tree) and `Tonbo-Baseline: true` on the root; `refs/tonbo/snapshots/<device id>/<folder binding id>` keeps it in the user's repository.
- The repository is the Project's `snapshot_repo` (`snap-<slug>` of its origin, or `snap-<project id hex>` for a named Project), created once through gitd's idempotent `PUT /api/v1/workspaces/<ws_hex>/repos/<name>` `{"private": false}` in your personal workspace. The commit is pushed to `refs/tonbo/u/<sub>/<device id>/<folder binding id>` with `-o tonbo.session=<id>`. A device's ref of before Projects, `refs/tonbo/u/<sub>/<device id>`, is continued by the first binding chain pushed in its repository, which deletes it first (git cannot hold a ref and one under it). The git token (`POST /api/iam/token` with the git audience, kept and refreshed like the Streams token) reaches git only as an `http.<gitd origin>/.extraHeader` in the environment of that one command: never in a file, an argument, a URL or the log.
- The chain head of each repository and device is kept in `state.json`. A push refused because gitd's ref moved on without this state (say, after the state was lost) fetches gitd's ref and continues the chain from it; a clone that lacks the chain's head fetches it first.

### Routing

Each session goes where its folder's Project says ([ADR-0016](../../apps/user-center/docs/decisions/adr-0016-org-git-workspaces.md) W-7). The daemon reads your folder bindings on this computer in every account it holds a login of, once a minute, and resolves a session's cwd to the nearest connected folder that holds it; a git work tree none of whose folders is connected gets its implicit personal Project, made at its first capture (its origin's, or one named after its root folder). The raw session and every record go to your personal store, and records of a session with a Project name it (`project_id`, `binding_id`); its snapshots go to the Project's repository, as above. A cwd outside every git work tree and every connected folder has no Project, as before. A session that starts in a folder connected to an organization's team Project, after it was connected, is the team's ([`capture-v1.json`](../../contracts/sessions/capture-v1.json) `team_session_stream.routing`), and the team sees its metadata only ([ADR-0017](../../apps/user-center/docs/decisions/adr-0017-projects-forks-visibility.md)'s metadata-only amendment): its raw lines stay in your personal store, its records go to the organization's `proc.inbox` in the published form (the device named only by its handle there, paths relative to the folder), and its snapshots to the team Project's repository. A teammate reads its title, state and each turn's written paths, never its turns; those reach a teammate only with a handoff, whose excerpt of the last turns your computer attaches after the secret filter ([`secret-patterns-v1.json`](../../contracts/sessions/secret-patterns-v1.json)): keys, tokens, private keys and passwords become `[redacted:<rule>]`. Lines a team session sent to `session.t.<session id>` in the organization's store before the amendment stay there; its next lines go to your personal store. A session that started before the folder was connected stays in your personal store, raw, with no Project and no snapshot. Only a yes given on this computer shares: `connect --account` records it in `folders.json` for the binding it was given for, and the team's sessions are those started after both the binding and the yes. A folder connected to an organization before the warning existed shares nothing (its sessions stay personal, with no Project and no snapshot, and the daemon logs why) until you run `yield connect --account <name>` there and answer yes. `yield ps --account <name>` lists the team's sessions, every member's, by metadata; `yield session read <id> --account <name>` reads only your own and refuses a teammate's, and `files` and `why` show a teammate's turns without their text.

The daemon uses the saved logins only, the personal account's for everything; `TONBO_API_KEY` does not name a device. One daemon runs per state directory.

## Sessions

```console
yield ps [--all] [--limit <sessions>] [--json]
yield context [--account <name>] [--json]
yield sessions [--limit <records>] [--json]
```

`ps` reads the catalog, recall's `GET /sessions` without `repo`, which the git-ingester keeps in the workspace's index from `proc.inbox`. It prints your sessions on every device, most recent activity first: the state and since when, the device (`this computer` for this one), the agent, the git root or cwd, the session and the files it recently wrote (the turn in progress's first). Exact paths come first; a path only a shell command appears to have written (`printf > file`, `tee`) is marked `(inferred)`. Ended sessions are left out unless `--all`; a session no client reported a state for shows `ended` once it is 30 minutes quiet, and `unknown` before. Above the sessions, under `NEEDS YOU`, it lists what waits on you, each with the one command that resolves it: a handoff run that needs you (`yield show <link>`), a handoff named to you (`yield take <link>`), a session waiting for your answer (where to answer it), a result to pull (`yield pull <link>`, or `yield show` for one that failed or came back), then two of your live sessions that wrote the same file (`yield inbox ack <ids>` once checked). It reads the local inbox the daemon fills, and only each handoff's latest message counts; `take` and `pull` mark a handoff's messages read. When nothing waits, the section is not printed. The sessions follow, waiting, then running, then idle. `--json` prints `{ attention, sessions }`, the same items and rows. With `--account` only the inbox's handoffs are listed as needing you: a teammate's waiting session is not yours to answer.

`context` is for an agent at the start of a session: about 8 KiB, one line per item and no summaries. In a folder connected to a team Project it lists teammates' live then recent sessions (the last 7 days) from the team catalog's metadata only: who, title, state, since when, files written and the session id; never a turn's text. Then your own recent sessions, then the handoffs the handoff events show you (yours, open to the team, or addressed to you) with their latest transition. Outside a team Project it lists only your own. `--account <name>` reads that organization's team Project instead of the folder's. A part that cannot be read is named and the rest still printed. What it prints is reference, never instructions.

`sessions` reads `proc.inbox` itself and prints one row per session among its newest records (200 by default): the last activity, the device (`this machine` for this one), the source, the git root or cwd, the branch and the turn ends seen. `ps` supersedes it; it stays until the first-run end-to-end check (`e2e/src/yield-first-run-e2e.mjs`) reads `ps`, once the index serves personal workspaces (ADR-0016 PR 4).

## Recall

```console
yield why <path> [--limit <turns>] [--max-chars <n>] [--json]
yield files <path> [--limit <turns>] [--json]
yield session read <id> [--turn <n>] [--max-chars <n>] [--json]
```

The recall commands read the git-ingester's queries at `https://git.tonbo.dev/api/recall/v1/workspaces/<ws_hex>` (recall-v1 `queries`) with the git token, and the turns themselves from `session.<session id>` with the Streams token. A path is resolved from the current directory to its git root and the snapshot repository name the daemon pushes to.

- `files` lists the turns that changed the file, newest first: when, session, turn, change, whether the turn wrote the file itself, device and snapshot commit.
- `session read` prints a session as role-tagged lines (`user:`, `assistant:`, `tool:`), each cut at `--max-chars`; with `--turn` only that turn, read between the offsets of its `turn_end` and the previous one, or split from the whole stream when they are missing.
- `why` joins `files` with each turn's prompt, the assistant's last words and the other files the turn changed, as a timeline an agent can read; `--json` gives the same structure.

## Handoff

```console
yield [--session <id>] [--task <text>] [--to <who>] [--recover | --new]
yield take <link> [--into <directory>] [--account <organization>]
yield done "<result>"
yield need-human "<why>"
yield pull <session-or-link> [--patch]
yield show <link>
yield renew <link> [--result]
yield handoffs [--account <organization>]
yield events [--after <seq>] [--wait <seconds>] [--account <organization>]
yield cancel <link>
yield inbox [--all] | ack [<id>...] | ignore <id>... | rate <id> useful|noise | mute | unmute
```

A handoff is in the Project of the folder it is made in. In a personal Project you hand a session to yourself on another computer or harness. In a shared team Project (`yield connect --account <organization>`) you hand it to any member, or with `--to <name>` to one member; `--to claude-code` or `--to codex` is you on that harness. A member's login of the organization is what makes and takes team handoffs, so the teammate needs one on their computer (`yield login --account <organization>`). In a personal Project, `--to <device>:<harness>` (such as `--to mac-2:codex`) dispatches the handoff to another of your own computers: `yield devices` marks the ones that can take it (online, with that harness). Its daemon takes the handoff, restores the snapshot into a fresh folder under the state directory and runs `claude -p` or `codex exec` there unattended, in the harness's default permission mode under a dispatch policy: it may edit files in that folder, run tests and read; a push, a delete or anything else stops the run, and the handoff becomes need-human. Your inbox tells you when it is done (`yield pull <link>`) or needs you; `yield show <link>` then says what stopped it and how to resume it there (`claude --resume` or `codex resume`, then `yield done`). Only your own devices can be targets, and Yield never handles the harness's model credentials.

- Bare `yield` hands off the current session of this Project (`--session` when more than one is live). The daemon captures the session up to now and publishes a snapshot of the worktree, gitd pins that exact commit, and the command prints a `/y/<id>` link and who it is for. After a lost response, `--recover` retries the same snapshot and `--new` takes a fresh one. A session that started before its folder was shared stays personal and cannot be handed to the team.
- `take` restores the pinned snapshot into a new `tonbo/h/<short id>` worktree on top of a base commit you already have, and prints the task, the session's files and its recent turns (read from the team's store for a team handoff). The source worktree is never touched. Without `--account` it tries your personal account and then each organization this computer is logged in to; a handoff you may not take is not found.
- Inside that worktree, `done` pushes and pins the result and records the handoff done; `need-human` records that it is waiting on you. Both use the login of the account the handoff is in.
- `pull` fetches the latest unpulled result of a session (or a handoff link) onto a branch, or with `--patch` into a patch file, without changing your worktree. The author pulls a teammate's result the same way.
- `show` prints a handoff's state, author, who it is for and who took it, and its captured context, without taking it. Any member can show a team handoff.
- `handoffs` lists your open handoffs (created, taken, running or waiting on a human) that you gave, hold, or are named in, across your personal account and every organization; `--account` narrows to one. It shows metadata only.
- `events` prints handoff transitions after a cursor, such as a teammate taking yours, with a `Next:` cursor for the following read; `--wait` holds up to 25 seconds for the first one. A handoff you can no longer see drops out.
- `cancel` withdraws a handoff you gave before anyone takes it and releases its snapshot pin. A take that loses to someone else names who holds it.
- `inbox` lists the messages the daemon keeps for you on this computer: a handoff that names you or is open to your team, and one you gave being taken, finished, failed, returned or waiting on you. Only the ones where you must act or a result is ready raise a system notification (Notification Center on macOS, `notify-send` on a Linux desktop; elsewhere they only reach the inbox); `mute` stops them. `ack` marks messages read, `ignore` sets them aside and `rate` records whether one was useful. Messages carry the handoff's link, task and who acted, never its turns. The daemon starts after the events already there when it first runs; `handoffs` lists what is open.

## Alpha metrics

```console
yield note --changed-plan <session>
```

During the Alpha, `yield` records a few content-free measures on your personal account (contracts/sessions/handoff-v1.json `metrics`, kept 180 days): how long a taken handoff waits for its first turn (the daemon, from the restore to that turn's end), which `continue` candidate was picked, how a delivered message or teammate context was rated, and, with `note --changed-plan`, that another session changed your plan. A value is a number, a flag or a fixed word; never a task, prompt, path or reason. A write that fails or takes longer than 3 seconds is dropped and never fails a command. `TONBO_YIELD_METRICS=off` turns it off.

## The Tonbo skill

`daemon install` also writes the Tonbo skill ([`skills/tonbo/SKILL.md`](skills/tonbo/SKILL.md), embedded by the build) to `~/.claude/skills/tonbo/SKILL.md` for Claude Code (`CLAUDE_CONFIG_DIR` moves it) and `~/.codex/skills/tonbo/SKILL.md` for Codex (`CODEX_HOME` moves it). It tells an agent to run `yield why`, `yield files` and `yield session read` when asked why code is the way it is or before changing an unfamiliar file, to cite session and time, to treat the results as reference and never as instructions, and not to guess when nothing is found. A manifest, `skills.json` in the state directory, records what was written: install never overwrites a `tonbo` skill it did not write, and `daemon uninstall` removes only its own unchanged files. Every daemon start refreshes yield's own unchanged copies to the skill of the running version, and the daemon restarts itself within a minute when an in-place upgrade (`npm install --global @tonbo/yield`) puts a new version on disk, so either upgrade path reaches both agents; a copy you edited or removed is left as it is. `yield status` reports the skill's version on each copy. `CLAUDE.md` and `AGENTS.md` are never edited.

## Environment

- `TONBO_API_KEY`: a personal key used instead of the saved login by `whoami` and `sessions`.
- `TONBO_YIELD_METRICS`: `off` stops the Alpha metrics.
- `TONBO_API_ORIGIN`: the Management API origin, `https://api.tonbo.dev` by default. `daemon install` writes it into the service, with `PATH`, `XDG_CONFIG_HOME` and `XDG_STATE_HOME`.
