@tonbo/yield 0.1.2 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +43 -11
- package/dist/bin/yield.js +4808 -1420
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
# Tonbo Yield
|
|
2
2
|
|
|
3
|
-
`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
|
|
3
|
+
`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 live session to another of your computers.
|
|
4
4
|
|
|
5
5
|
Install it on macOS (arm64, x64) or Linux with glibc (x64, arm64) with one line, without sudo and without Node.js:
|
|
6
6
|
|
|
7
7
|
```console
|
|
8
|
-
curl -fsSL https://tonbo.
|
|
8
|
+
curl -fsSL https://yield.tonbo.io/install.sh | sh
|
|
9
9
|
```
|
|
10
10
|
|
|
11
|
-
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. 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://tonbo.
|
|
11
|
+
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. 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.
|
|
12
12
|
|
|
13
13
|
Or install the public package with Node.js 22 or newer:
|
|
14
14
|
|
|
@@ -26,11 +26,20 @@ yield whoami [--json]
|
|
|
26
26
|
yield logout
|
|
27
27
|
```
|
|
28
28
|
|
|
29
|
-
`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
|
|
29
|
+
`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.
|
|
30
|
+
|
|
31
|
+
## Projects
|
|
32
|
+
|
|
33
|
+
```console
|
|
34
|
+
yield connect [folder] [--name <name> | --project <id>] [--account <name>] [--json]
|
|
35
|
+
yield disconnect [folder]
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
`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 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. 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.
|
|
30
39
|
|
|
31
40
|
## The device id
|
|
32
41
|
|
|
33
|
-
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 (`refs/tonbo/u/<sub>/<device id>`) name it.
|
|
42
|
+
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.
|
|
34
43
|
|
|
35
44
|
`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.
|
|
36
45
|
|
|
@@ -66,14 +75,18 @@ The capture follows [`contracts/sessions/capture-v1.json`](../../contracts/sessi
|
|
|
66
75
|
|
|
67
76
|
### Snapshots
|
|
68
77
|
|
|
69
|
-
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
|
|
78
|
+
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.
|
|
70
79
|
|
|
71
|
-
- The tree is
|
|
72
|
-
- 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 its upstream, or HEAD) and `Tonbo-Baseline: true` on the root; `refs/tonbo/snapshots/<device id>` keeps it in the user's repository.
|
|
73
|
-
- The repository is `snap-<slug>` of
|
|
80
|
+
- 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.
|
|
81
|
+
- 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 its upstream, or HEAD) and `Tonbo-Baseline: true` on the root; `refs/tonbo/snapshots/<device id>/<folder binding id>` keeps it in the user's repository.
|
|
82
|
+
- 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.
|
|
74
83
|
- 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.
|
|
75
84
|
|
|
76
|
-
|
|
85
|
+
### Routing
|
|
86
|
+
|
|
87
|
+
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 folder connected to an organization's Project is captured into your personal store with no Project and no snapshot for now: what reaches the organization is being redesigned.
|
|
88
|
+
|
|
89
|
+
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.
|
|
77
90
|
|
|
78
91
|
## Sessions
|
|
79
92
|
|
|
@@ -82,7 +95,7 @@ yield ps [--all] [--limit <sessions>] [--json]
|
|
|
82
95
|
yield sessions [--limit <records>] [--json]
|
|
83
96
|
```
|
|
84
97
|
|
|
85
|
-
`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). 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.
|
|
98
|
+
`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.
|
|
86
99
|
|
|
87
100
|
`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).
|
|
88
101
|
|
|
@@ -100,6 +113,25 @@ The recall commands read the git-ingester's queries at `https://git.tonbo.dev/ap
|
|
|
100
113
|
- `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.
|
|
101
114
|
- `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.
|
|
102
115
|
|
|
116
|
+
## Handoff
|
|
117
|
+
|
|
118
|
+
```console
|
|
119
|
+
yield [--session <id>] [--task <text>] [--recover | --new]
|
|
120
|
+
yield take <link> [--into <directory>]
|
|
121
|
+
yield done "<result>"
|
|
122
|
+
yield need-human "<why>"
|
|
123
|
+
yield pull <session-or-link> [--patch]
|
|
124
|
+
yield show <link>
|
|
125
|
+
yield renew <link> [--result]
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
Handoff is personal in this release: you hand a session to yourself on another computer under the same account.
|
|
129
|
+
|
|
130
|
+
- 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. After a lost response, `--recover` retries the same snapshot and `--new` takes a fresh one.
|
|
131
|
+
- `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 session's recent turns. The source worktree is never touched.
|
|
132
|
+
- Inside that worktree, `done` pushes and pins the result and records the handoff done; `need-human` records that it is waiting on you.
|
|
133
|
+
- `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.
|
|
134
|
+
|
|
103
135
|
## The Tonbo skill
|
|
104
136
|
|
|
105
137
|
`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. `CLAUDE.md` and `AGENTS.md` are never edited.
|