@bivy/bivy 0.15.0-staging.2 → 0.15.0-staging.20

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 CHANGED
@@ -117,10 +117,16 @@ curl -fsSL https://bivy.sh/install.sh | bash
117
117
 
118
118
  macOS and Linux. Requires Node.js 20 or newer. The installer puts the
119
119
  [`@bivy/bivy`](https://www.npmjs.com/package/@bivy/bivy) npm package and the
120
- `bivy` command on your `PATH` with optional bridges skipped for speed, then runs the guided `bivy setup` wizard — agent
121
- choice, selected-agent install if needed, remote access, and an auto-start background service (launchd on macOS,
122
- systemd on Linux). Re-running it on a machine that already has Bivy just applies
123
- the latest build and restarts the service.
120
+ `bivy` command on your `PATH` with optional bridges skipped for speed, then runs
121
+ `bivy setup` — agent choice, selected-agent install if needed, remote access,
122
+ and an auto-start background service (launchd on macOS, systemd on Linux).
123
+
124
+ If Claude Code, Codex, OpenCode, Gemini, or another agent is already installed,
125
+ setup says so and uses that existing CLI, login, and configuration. Bivy does
126
+ not replace the agent or copy its native credentials; it adds remote continuity
127
+ and governance around the agent you already use. Re-running the installer on a
128
+ machine that already has Bivy just applies the latest build and restarts the
129
+ service.
124
130
 
125
131
  **What needs an account, and what doesn't.** The CLI alone — `bivy run`,
126
132
  `bivy resume`, `bivy sessions` — needs no account and no server; `bivy setup`
@@ -131,11 +137,19 @@ UI needs a control plane, because the node hosts none: use the hosted one at
131
137
  [self-host your own](docs/self-host-quickstart.md). Switch any time with
132
138
  `bivy relay:setup`.
133
139
 
140
+ Prefer to inspect the installer first?
141
+
142
+ ```bash
143
+ curl -fsSL https://bivy.sh/install.sh -o install.sh
144
+ less install.sh
145
+ bash install.sh
146
+ ```
147
+
134
148
  **What the installer does with sudo.** It escalates only when it must, and
135
149
  tells you when it does:
136
150
 
137
- - Debian/Ubuntu without a suitable Node.js: `sudo apt-get install build-essential
138
- python3 curl`, then NodeSource's Node 22 setup script via `sudo`.
151
+ - Debian/Ubuntu without a suitable Node.js: `sudo apt-get install curl
152
+ ca-certificates`, then NodeSource's Node 22 setup script via `sudo`.
139
153
  - Other Linux, or macOS, without a suitable Node.js: downloads the official
140
154
  Node 22 tarball from nodejs.org (sha256-checked) and installs it under
141
155
  `/usr/local` with `sudo`.
@@ -267,9 +281,9 @@ See [`docs/remote-access.md`](docs/remote-access.md) and
267
281
 
268
282
  ## Supported agents
269
283
 
270
- **Claude Code and Codex are the recommended, release-certified paths.** The
271
- broader catalog stays available under **More agents**; capabilities and fidelity
272
- vary by runtime.
284
+ **Bivy maintains wrappers for the popular coding agents below.** Claude Code,
285
+ Codex, Pi, and OpenCode have the richest release-tested capability sets today;
286
+ capabilities and fidelity still vary by runtime.
273
287
 
274
288
  | Agent | Command | Notes |
275
289
  |---|---|---|
@@ -6,7 +6,7 @@
6
6
  "command": "codex",
7
7
  "hidden": true,
8
8
  "supportTier": "supported",
9
- "certification": "unverified",
9
+ "certification": "adapter-tested",
10
10
  "headlessFlags": [
11
11
  "exec"
12
12
  ],
@@ -37,7 +37,7 @@
37
37
  "label": "Aider",
38
38
  "command": "aider",
39
39
  "hidden": false,
40
- "supportTier": "beta",
40
+ "supportTier": "supported",
41
41
  "certification": "adapter-tested",
42
42
  "headlessFlags": [
43
43
  "--yes-always",
@@ -53,8 +53,8 @@
53
53
  "label": "Hermes",
54
54
  "command": "hermes",
55
55
  "hidden": true,
56
- "supportTier": "beta",
57
- "certification": "adapter-tested",
56
+ "supportTier": "experimental",
57
+ "certification": "unverified",
58
58
  "headlessFlags": [],
59
59
  "install": {
60
60
  "kind": "npm",
@@ -66,7 +66,7 @@
66
66
  "label": "Goose",
67
67
  "command": "goose",
68
68
  "hidden": false,
69
- "supportTier": "beta",
69
+ "supportTier": "supported",
70
70
  "certification": "adapter-tested",
71
71
  "headlessFlags": [
72
72
  "run",
@@ -87,7 +87,7 @@
87
87
  "label": "Gemini CLI",
88
88
  "command": "gemini",
89
89
  "hidden": false,
90
- "supportTier": "beta",
90
+ "supportTier": "supported",
91
91
  "certification": "adapter-tested",
92
92
  "headlessFlags": [
93
93
  "-p",
@@ -105,7 +105,7 @@
105
105
  "label": "Qwen Code",
106
106
  "command": "qwen",
107
107
  "hidden": false,
108
- "supportTier": "beta",
108
+ "supportTier": "supported",
109
109
  "certification": "adapter-tested",
110
110
  "headlessFlags": [
111
111
  "-p",
@@ -123,7 +123,7 @@
123
123
  "label": "Cline",
124
124
  "command": "cline",
125
125
  "hidden": false,
126
- "supportTier": "beta",
126
+ "supportTier": "supported",
127
127
  "certification": "adapter-tested",
128
128
  "headlessFlags": [
129
129
  "-y",
@@ -139,7 +139,7 @@
139
139
  "label": "Crush",
140
140
  "command": "crush",
141
141
  "hidden": false,
142
- "supportTier": "beta",
142
+ "supportTier": "supported",
143
143
  "certification": "adapter-tested",
144
144
  "headlessFlags": [
145
145
  "run",
@@ -155,7 +155,7 @@
155
155
  "label": "Cursor",
156
156
  "command": "cursor-agent",
157
157
  "hidden": false,
158
- "supportTier": "beta",
158
+ "supportTier": "supported",
159
159
  "certification": "adapter-tested",
160
160
  "headlessFlags": [
161
161
  "--force",
@@ -172,7 +172,7 @@
172
172
  "label": "GitHub Copilot",
173
173
  "command": "copilot",
174
174
  "hidden": false,
175
- "supportTier": "beta",
175
+ "supportTier": "supported",
176
176
  "certification": "adapter-tested",
177
177
  "headlessFlags": [
178
178
  "--allow-all-tools",
@@ -188,7 +188,7 @@
188
188
  "label": "Grok",
189
189
  "command": "grok",
190
190
  "hidden": false,
191
- "supportTier": "beta",
191
+ "supportTier": "supported",
192
192
  "certification": "adapter-tested",
193
193
  "headlessFlags": [
194
194
  "-p",
@@ -205,7 +205,7 @@
205
205
  "label": "Amp",
206
206
  "command": "amp",
207
207
  "hidden": false,
208
- "supportTier": "beta",
208
+ "supportTier": "supported",
209
209
  "certification": "adapter-tested",
210
210
  "headlessFlags": [
211
211
  "-x",
@@ -222,7 +222,7 @@
222
222
  "label": "Auggie",
223
223
  "command": "auggie",
224
224
  "hidden": false,
225
- "supportTier": "beta",
225
+ "supportTier": "supported",
226
226
  "certification": "adapter-tested",
227
227
  "headlessFlags": [
228
228
  "--quiet",
@@ -238,7 +238,7 @@
238
238
  "label": "Droid",
239
239
  "command": "droid",
240
240
  "hidden": false,
241
- "supportTier": "beta",
241
+ "supportTier": "supported",
242
242
  "certification": "adapter-tested",
243
243
  "headlessFlags": [
244
244
  "exec",
@@ -256,7 +256,7 @@
256
256
  "label": "Continue",
257
257
  "command": "cn",
258
258
  "hidden": false,
259
- "supportTier": "beta",
259
+ "supportTier": "supported",
260
260
  "certification": "adapter-tested",
261
261
  "headlessFlags": [
262
262
  "--auto",
@@ -272,7 +272,7 @@
272
272
  "label": "Kilo Code",
273
273
  "command": "kilo",
274
274
  "hidden": false,
275
- "supportTier": "beta",
275
+ "supportTier": "supported",
276
276
  "certification": "adapter-tested",
277
277
  "headlessFlags": [
278
278
  "run",
@@ -289,7 +289,7 @@
289
289
  "label": "Rovo Dev",
290
290
  "command": "acli",
291
291
  "hidden": false,
292
- "supportTier": "beta",
292
+ "supportTier": "supported",
293
293
  "certification": "adapter-tested",
294
294
  "headlessFlags": [
295
295
  "rovodev",
package/bin/bivy.mjs CHANGED
@@ -543,6 +543,11 @@ function commandExists(cmd) {
543
543
  return runQuiet("sh", ["-lc", "command -v -- \"$1\" >/dev/null 2>&1", "sh", cmd]).code === 0;
544
544
  }
545
545
 
546
+ function commandOnPath(cmd) {
547
+ const result = runQuiet("sh", ["-lc", "command -v -- \"$1\"", "sh", cmd]);
548
+ return result.code === 0 ? result.stdout.trim().split(/\r?\n/)[0] || "" : "";
549
+ }
550
+
546
551
  function npmGlobalBinCommand(cmd) {
547
552
  if (!commandExists("npm")) return "";
548
553
  const prefix = runQuiet("npm", ["prefix", "-g"]);
@@ -552,9 +557,13 @@ function npmGlobalBinCommand(cmd) {
552
557
  return fs.existsSync(candidate) ? candidate : "";
553
558
  }
554
559
 
560
+ function nodeAtLeast(minMajor, minMinor = 0) {
561
+ const [major, minor] = process.versions.node.split(".").map(Number);
562
+ return major > minMajor || (major === minMajor && minor >= minMinor);
563
+ }
564
+
555
565
  function hasSupportedNode() {
556
- const [major] = process.versions.node.split(".").map(Number);
557
- return major >= 20;
566
+ return nodeAtLeast(20);
558
567
  }
559
568
 
560
569
  async function ensureDeps() {
@@ -612,8 +621,15 @@ function nodePackageInstalled(packageName) {
612
621
  return runQuiet(nodeBin, ["-e", "require.resolve(process.argv[1], { paths: [process.argv[2]] })", packageName, repoRoot]).code === 0;
613
622
  }
614
623
 
624
+ function nodePackageLoadable(packageName) {
625
+ return runQuiet(nodeBin, ["-e", "require(require.resolve(process.argv[1], { paths: [process.argv[2]] }))", packageName, repoRoot]).code === 0;
626
+ }
627
+
615
628
  async function ensureNodePackage(packageName) {
616
- if (nodePackageInstalled(packageName)) return true;
629
+ if (nodePackageInstalled(packageName)) {
630
+ console.log(c.green(` ✓ Found Bivy bridge package ${packageName}`));
631
+ return true;
632
+ }
617
633
  // Add with the package manager that owns this tree. Running `npm install` in a
618
634
  // pnpm workspace would write a competing package-lock.json and a hoisted
619
635
  // node_modules over pnpm's symlink layout, leaving the checkout in a state
@@ -626,7 +642,7 @@ async function ensureNodePackage(packageName) {
626
642
  console.error(c.red(`${cmd} is required to install ${packageName}.`));
627
643
  return false;
628
644
  }
629
- console.log(c.dim(`Installing ${packageName}…`));
645
+ console.log(c.dim(`Installing Bivy bridge package ${packageName}…`));
630
646
  const code = await run(cmd, [...baseArgs, packageName], { cwd: repoRoot });
631
647
  return code === 0 && nodePackageInstalled(packageName);
632
648
  }
@@ -634,13 +650,18 @@ async function ensureNodePackage(packageName) {
634
650
  const userLocalPrefix = process.env.BIVY_NPM_GLOBAL_PREFIX || path.join(os.homedir(), ".local");
635
651
 
636
652
  async function ensureNpmCommand(command, packageName, label) {
637
- if (commandExists(command)) return true;
653
+ const existing = commandOnPath(command);
654
+ if (existing) {
655
+ console.log(c.green(` ✓ Found existing ${label}: ${existing}`));
656
+ console.log(c.dim(` Bivy will use your installed ${label}; it will not replace its auth or configuration.`));
657
+ return true;
658
+ }
638
659
  if (!commandExists("npm")) {
639
660
  console.log(c.yellow(`Skipping ${label}: npm is not available.`));
640
661
  return false;
641
662
  }
642
663
  fs.mkdirSync(path.join(userLocalPrefix, "bin"), { recursive: true });
643
- console.log(c.dim(`Installing ${label} (${packageName})…`));
664
+ console.log(c.dim(`Installing ${label} (${packageName}) because it was not found on PATH…`));
644
665
  const code = await run("npm", ["install", "--global", "--prefix", userLocalPrefix, packageName, "--no-audit", "--no-fund"]);
645
666
  return code === 0 && commandExists(command);
646
667
  }
@@ -4121,6 +4142,14 @@ async function cmdDoctor(args = []) {
4121
4142
 
4122
4143
  console.log(c.bold("\n Bivy doctor\n"));
4123
4144
  console.log(` ${mark(hasSupportedNode())} Node ${process.version}${hasSupportedNode() ? "" : c.dim(" (needs >= 20.0.0)")}`);
4145
+ const ptyInstalled = nodePackageInstalled("node-pty");
4146
+ const ptyUsable = ptyInstalled && nodePackageLoadable("node-pty");
4147
+ console.log(` ${mark(ptyUsable, true)} terminal PTY ${ptyUsable ? c.green("available") : ptyInstalled ? c.yellow("installed but not loadable — reinstall after installing build tools") : c.dim("optional dependency missing — interactive terminals unavailable; chat/exec still work")}`);
4148
+ const claudeBridge = nodePackageInstalled("@anthropic-ai/claude-agent-sdk");
4149
+ console.log(` ${mark(claudeBridge, true)} Claude bridge ${claudeBridge ? c.green("installed") : c.dim("optional — installed on first Claude setup/use")}`);
4150
+ const piBridge = nodePackageInstalled("@earendil-works/pi-coding-agent");
4151
+ const piNodeOk = nodeAtLeast(22, 19);
4152
+ console.log(` ${mark(piBridge && piNodeOk, true)} Pi bridge ${piBridge ? (piNodeOk ? c.green("installed") : c.yellow("installed but needs Node >=22.19")) : c.dim("optional — installed only if you choose Pi")}`);
4124
4153
  console.log(` ${mark(commandExists("git"), true)} git${commandExists("git") ? "" : c.dim(" (recommended for repo-backed sessions)")}`);
4125
4154
  // GitHub is optional (a "No repo" session needs none), so this only ever warns.
4126
4155
  // `gh` is NOT required — it's a token fallback; the primary path is Bivy's own
@@ -4234,6 +4263,51 @@ async function tailFiles(files, lines, follow) {
4234
4263
  // alive. Keeping stdout pointed directly at the log lets the update survive the
4235
4264
  // node restart; polling it here still gives the web terminal live progress up to
4236
4265
  // the moment its PTY disappears.
4266
+ function spawnDetachedUpdateProcess(args, logFd) {
4267
+ const env = { ...process.env, BIVY_UPDATE_DETACHED: "1" };
4268
+ const updateArgs = [selfScript, "update", ...args];
4269
+ const { kind } = servicePaths();
4270
+
4271
+ // A Bivy web terminal is a child of bivy.service. A plain detached child still
4272
+ // stays in that systemd cgroup, so the installer's early `bivy stop` would kill
4273
+ // the updater along with the node before npm/install runs. When a user systemd
4274
+ // manager is available, launch the updater as its own transient unit instead;
4275
+ // stopping/restarting bivy.service then leaves the updater alive.
4276
+ if (kind === "systemd" && commandExists("systemd-run")) {
4277
+ const unit = `bivy-update-${process.pid}-${Date.now()}`.replace(/[^A-Za-z0-9_.@-]/g, "-");
4278
+ const logProp = `append:${updateLogPath}`;
4279
+ const child = spawn("systemd-run", [
4280
+ "--user",
4281
+ "--wait",
4282
+ "--collect",
4283
+ `--unit=${unit}`,
4284
+ `--working-directory=${repoRoot}`,
4285
+ `--property=StandardOutput=${logProp}`,
4286
+ `--property=StandardError=${logProp}`,
4287
+ "--setenv=BIVY_UPDATE_DETACHED=1",
4288
+ `--setenv=BIVY_DATA_DIR=${appDir}`,
4289
+ `--setenv=PATH=${process.env.PATH || ""}`,
4290
+ nodeBin,
4291
+ ...updateArgs,
4292
+ ], {
4293
+ detached: true,
4294
+ stdio: ["ignore", logFd, logFd],
4295
+ env: { ...env, ...systemdUserEnv() },
4296
+ });
4297
+ child.unref();
4298
+ return child;
4299
+ }
4300
+
4301
+ const child = spawn(nodeBin, updateArgs, {
4302
+ cwd: repoRoot,
4303
+ detached: true,
4304
+ stdio: ["ignore", logFd, logFd],
4305
+ env,
4306
+ });
4307
+ child.unref();
4308
+ return child;
4309
+ }
4310
+
4237
4311
  async function showDetachedUpdateProgress(child, start) {
4238
4312
  let offset = start;
4239
4313
  let finished = false;
@@ -4289,13 +4363,7 @@ async function cmdUpdate(args = []) {
4289
4363
  const logFd = fs.openSync(updateLogPath, "a");
4290
4364
  const logStart = fs.fstatSync(logFd).size;
4291
4365
  fs.writeSync(logFd, `\n=== bivy update started ${new Date().toISOString()} ===\n`);
4292
- const child = spawn(nodeBin, [selfScript, "update", ...args], {
4293
- cwd: repoRoot,
4294
- detached: true,
4295
- stdio: ["ignore", logFd, logFd],
4296
- env: { ...process.env, BIVY_UPDATE_DETACHED: "1" },
4297
- });
4298
- child.unref();
4366
+ const child = spawnDetachedUpdateProcess(args, logFd);
4299
4367
  fs.closeSync(logFd);
4300
4368
  console.log(c.green("Update started in the background. Showing progress until the node restarts…"));
4301
4369
  console.log(c.dim(`The terminal will reconnect automatically. Run ${c.cyan("bivy update:log")} afterward for the final output.`));
@@ -5052,8 +5120,10 @@ Unlike 'bivy run', these commands operate on governed background Runs with check
5052
5120
  }
5053
5121
  const remote = await openRemoteApp();
5054
5122
  if (!remote) {
5055
- console.log("No remote access configured yet.");
5056
- console.log(`Run ${c.cyan("bivy relay:setup")} to enable the web/PWA app, then ${c.cyan("bivy open")}.`);
5123
+ console.log(c.yellow("Remote access is not configured on this machine yet."));
5124
+ console.log("The local node hosts only an API/WebSocket, not the Bivy PWA.");
5125
+ console.log(`Run ${c.cyan("bivy relay:setup")} to connect this node to the hosted or self-hosted remote web/PWA app, then run ${c.cyan("bivy open")} again.`);
5126
+ process.exitCode = 1;
5057
5127
  } else if (!canOpenBrowser()) {
5058
5128
  console.log(`Open the Bivy app here: ${c.cyan(remote.remoteBase)}`);
5059
5129
  }
@@ -40,6 +40,15 @@ export function piCommandAvailable() {
40
40
  export function invalidatePiCommandProbe() {
41
41
  PI_COMMAND_CACHE.clear();
42
42
  }
43
+ export function piBridgeInstalled() {
44
+ try {
45
+ import.meta.resolve("@earendil-works/pi-coding-agent");
46
+ return true;
47
+ }
48
+ catch {
49
+ return false;
50
+ }
51
+ }
43
52
  function unsupportedNodeMessage() {
44
53
  return `Pi requires Node.js 22.19+ (found ${process.version}). Upgrade Node, or select another agent such as Claude Code/Codex/OpenCode.`;
45
54
  }
@@ -87,13 +96,15 @@ export function piIntegration(origin) {
87
96
  visible: true,
88
97
  origin,
89
98
  describe: () => {
90
- const installed = piCommandAvailable();
99
+ const commandInstalled = piCommandAvailable();
100
+ const bridgeInstalled = piBridgeInstalled();
101
+ const installed = commandInstalled && bridgeInstalled && nodeSupportsPi();
91
102
  return {
92
103
  id: "pi",
93
104
  executionMode: "protocol",
94
105
  displayName: "Pi",
95
106
  description: "The operator-installed Pi coding agent connected to Bivy for durable sessions, governance, packages, and model selection.",
96
- status: installed && nodeSupportsPi() ? "available" : "external",
107
+ status: installed ? "available" : "external",
97
108
  packageName: "@earendil-works/pi-coding-agent",
98
109
  language: "TypeScript",
99
110
  capabilities: PI_CAPABILITIES,
@@ -105,7 +116,9 @@ export function piIntegration(origin) {
105
116
  ? unsupportedNodeMessage()
106
117
  : installed
107
118
  ? "Uses the Pi command and agent-owned auth/configuration already on this node, and hands sessions back to that native TUI."
108
- : "Install and sign in to Pi on this node; Bivy will connect to that existing agent.",
119
+ : commandInstalled
120
+ ? "Pi is on PATH, but Bivy's optional Pi bridge is not installed. Select Pi in setup or run 'bivy agents:install'."
121
+ : "Install and sign in to Pi on this node; Bivy will connect to that existing agent.",
109
122
  install: installed ? undefined : {
110
123
  label: "Install Pi",
111
124
  description: "Installs the upstream Pi coding agent on this node.",
@@ -116,6 +129,8 @@ export function piIntegration(origin) {
116
129
  create: (options) => {
117
130
  if (!piCommandAvailable())
118
131
  throw new Error(`Pi command not found on PATH: ${piCommand()}`);
132
+ if (!piBridgeInstalled())
133
+ throw new Error("Bivy's optional Pi bridge is not installed. Run 'bivy setup' and choose Pi, or run 'bivy agents:install'.");
119
134
  // Vault-backed credentials (see catalogRuntimes): the daemon-hosted Pi
120
135
  // session reads the shared vault the user signed in to, not Pi's own
121
136
  // plaintext auth.json. The agent dir still supplies config/models/packages.
@@ -82,7 +82,7 @@ export const AGENT_PROFILES = {
82
82
  // git-aware turn and exits instead of dropping into the REPL.
83
83
  args: ["--yes-always", "--message"],
84
84
  promptMode: "argv",
85
- supportTier: "beta",
85
+ supportTier: "supported",
86
86
  authOwner: "mixed",
87
87
  blurb: "Popular git-native pair-programming agent (Aider).",
88
88
  // `aider --model <id> …` — a leading option (insertAt: 0). Aider resolves its
@@ -116,6 +116,7 @@ export const AGENT_PROFILES = {
116
116
  // Hidden from the picker: dumb-pipe adapter with no validated JSON parser or
117
117
  // documented session/resume flag (still runnable via BIVY_RUNTIME=hermes).
118
118
  hidden: true,
119
+ supportTier: "experimental",
119
120
  // No `resume`: no documented session/resume flag.
120
121
  install: { kind: "npm", pkg: "hermes-agent" },
121
122
  },
@@ -137,7 +138,7 @@ export const AGENT_PROFILES = {
137
138
  // BIVY_GOOSE_ACP=1 (or global BIVY_PREFER_ACP=1); off by default until validated.
138
139
  acp: { args: ["acp"] },
139
140
  promptMode: "argv",
140
- supportTier: "beta",
141
+ supportTier: "supported",
141
142
  blurb: "Block's open-source agent with a structured stream-json protocol (Goose).",
142
143
  // Homebrew isn't present on stock Linux nodes (brew → ENOENT); the official
143
144
  // download script installs the goose binary on both Linux and macOS.
@@ -154,7 +155,7 @@ export const AGENT_PROFILES = {
154
155
  displayName: "Gemini CLI",
155
156
  command: "gemini",
156
157
  packageName: "@google/gemini-cli",
157
- supportTier: "beta",
158
+ supportTier: "supported",
158
159
  blurb: "Google's terminal coding agent (Gemini CLI).",
159
160
  // `gemini -m <id> … -p "<prompt>"` — a leading option before the trailing `-p`
160
161
  // (insertAt: 0). The prompt flag stays last, so prepending is safe.
@@ -197,7 +198,7 @@ export const AGENT_PROFILES = {
197
198
  displayName: "Qwen Code",
198
199
  command: "qwen",
199
200
  packageName: "@qwen-code/qwen-code",
200
- supportTier: "beta",
201
+ supportTier: "supported",
201
202
  blurb: "Alibaba's Qwen Code CLI (a Gemini-CLI fork tuned for Qwen-Coder models).",
202
203
  // Gemini-CLI fork: same `-m <id> … -p` model flag (insertAt: 0).
203
204
  model: {
@@ -241,7 +242,7 @@ export const AGENT_PROFILES = {
241
242
  displayName: "Cline",
242
243
  command: "cline",
243
244
  packageName: "cline",
244
- supportTier: "beta",
245
+ supportTier: "supported",
245
246
  blurb: "Cline's standalone terminal agent (the CLI sibling of the Cline IDE extension).",
246
247
  // `cline -y "<prompt>"` runs one autonomous, non-interactive task (‑y/‑‑yolo
247
248
  // skips per-tool prompts so a piped run doesn't wedge on approval). Bivy's
@@ -264,7 +265,7 @@ export const AGENT_PROFILES = {
264
265
  displayName: "Crush",
265
266
  command: "crush",
266
267
  packageName: "@charmland/crush",
267
- supportTier: "beta",
268
+ supportTier: "supported",
268
269
  blurb: "Charm's glamourous open-source coding agent (Crush).",
269
270
  // `crush run "<prompt>"` runs a single non-interactive prompt and exits;
270
271
  // `-q/--quiet` suppresses the spinner UI so stdout is just the reply.
@@ -285,7 +286,7 @@ export const AGENT_PROFILES = {
285
286
  displayName: "Cursor",
286
287
  command: "cursor-agent",
287
288
  packageName: "cursor (curl https://cursor.com/install)",
288
- supportTier: "beta",
289
+ supportTier: "supported",
289
290
  blurb: "Cursor's standalone terminal coding agent (cursor-agent) — the editor's engine on the CLI.",
290
291
  // `cursor-agent --force -p "<prompt>"` runs one non-interactive print turn and
291
292
  // exits (`-p/--print`); `--force` auto-approves tool/command execution so a
@@ -320,7 +321,7 @@ export const AGENT_PROFILES = {
320
321
  displayName: "GitHub Copilot",
321
322
  command: "copilot",
322
323
  packageName: "@github/copilot",
323
- supportTier: "beta",
324
+ supportTier: "supported",
324
325
  blurb: "GitHub's official terminal coding agent (Copilot CLI).",
325
326
  // `copilot --allow-all-tools -p "<prompt>"` runs one programmatic turn and
326
327
  // exits; --allow-all-tools skips per-tool approval so a piped run doesn't
@@ -352,7 +353,7 @@ export const AGENT_PROFILES = {
352
353
  behaviors: { preflight: "grok", prepare: "grok-auth", nativeSessions: "grok" },
353
354
  command: "grok",
354
355
  packageName: "grok (curl -fsSL https://x.ai/cli/install.sh | bash)",
355
- supportTier: "beta",
356
+ supportTier: "supported",
356
357
  authOwner: "mixed",
357
358
  blurb: "xAI's official Grok coding agent (Grok CLI) — SuperGrok/X subscription or API key.",
358
359
  // Official CLI: `grok -p "<prompt>"` (alias `--single`) runs one headless
@@ -392,7 +393,7 @@ export const AGENT_PROFILES = {
392
393
  displayName: "Amp",
393
394
  command: "amp",
394
395
  packageName: "@sourcegraph/amp",
395
- supportTier: "beta",
396
+ supportTier: "supported",
396
397
  blurb: "Sourcegraph's autonomous coding agent with persistent threads (Amp).",
397
398
  // `amp -x "<prompt>"` (`--execute`) runs one thread turn and streams to stdout;
398
399
  // Amp doesn't gate tools per-run (governed by its own allowlist config), so no
@@ -414,7 +415,7 @@ export const AGENT_PROFILES = {
414
415
  displayName: "Auggie",
415
416
  command: "auggie",
416
417
  packageName: "@augmentcode/auggie",
417
- supportTier: "beta",
418
+ supportTier: "supported",
418
419
  blurb: "Augment Code's terminal agent backed by its codebase context engine (Auggie).",
419
420
  // `auggie --quiet --print "<prompt>"` runs one non-interactive turn and prints
420
421
  // the final reply (`--print`); `--quiet` drops the UI chatter. Prompt trails.
@@ -428,7 +429,7 @@ export const AGENT_PROFILES = {
428
429
  displayName: "Droid",
429
430
  command: "droid",
430
431
  packageName: "droid (curl https://app.factory.ai/cli)",
431
- supportTier: "beta",
432
+ supportTier: "supported",
432
433
  blurb: "Factory AI's autonomous terminal coding agent (Droid).",
433
434
  // `droid exec --auto high "<prompt>"` runs one headless task at high autonomy
434
435
  // (auto-approves) and streams to stdout. Prompt trails the `exec` subcommand.
@@ -456,7 +457,7 @@ export const AGENT_PROFILES = {
456
457
  displayName: "Continue",
457
458
  command: "cn",
458
459
  packageName: "@continuedev/cli",
459
- supportTier: "beta",
460
+ supportTier: "supported",
460
461
  blurb: "Continue's headless terminal agent (cn) driving configurable assistants.",
461
462
  // `cn --auto -p "<prompt>"` runs one headless turn (`-p` = no TUI) and prints
462
463
  // the final response; `--auto` allows all tools without prompting. Prompt
@@ -484,7 +485,7 @@ export const AGENT_PROFILES = {
484
485
  displayName: "Kilo Code",
485
486
  command: "kilo",
486
487
  packageName: "@kilocode/cli",
487
- supportTier: "beta",
488
+ supportTier: "supported",
488
489
  blurb: "Kilo Code's terminal CLI (an OpenCode fork) for pipeline-friendly agentic coding.",
489
490
  // `kilo run --auto "<prompt>"` runs one non-interactive turn (`run`) with
490
491
  // auto-approved permissions (`--auto`) and streams to stdout. Prompt trails.
@@ -518,7 +519,7 @@ export const AGENT_PROFILES = {
518
519
  displayName: "Rovo Dev",
519
520
  command: "acli",
520
521
  packageName: "atlassian acli (rovodev)",
521
- supportTier: "beta",
522
+ supportTier: "supported",
522
523
  blurb: "Atlassian's Rovo Dev terminal coding agent, run through the acli CLI.",
523
524
  // `acli rovodev run --yolo "<prompt>"` runs one instruction headlessly; --yolo
524
525
  // skips tool-approval prompts. Prompt trails the `rovodev run` subcommand.
@@ -3,9 +3,11 @@ export function certificationEntry(id) {
3
3
  return CERTIFICATION_MATRIX.agents.find((entry) => entry.id === id);
4
4
  }
5
5
  /**
6
- * The paid Supported promise is derived from certification, never merely copied
7
- * from a profile. A suspended/missing entry, wrong adapter mode, stale pin, or
8
- * missing required runtime capability is downgraded to Beta in the picker.
6
+ * Supported means Bivy maintains a wrapper for this integration. Certification is
7
+ * a separate fidelity signal: an active matrix entry verifies the richer release-
8
+ * tested capability set for a pinned adapter version, while drift or a different
9
+ * configured path keeps the wrapper Supported but marks the capability surface as
10
+ * adapter-tested instead of release-tested.
9
11
  */
10
12
  export function applyCertification(runtime) {
11
13
  if (runtime.supportTier !== "supported")
@@ -18,9 +20,8 @@ export function applyCertification(runtime) {
18
20
  if (!eligible) {
19
21
  return {
20
22
  ...runtime,
21
- supportTier: "beta",
22
- certification: "adapter-tested",
23
- notes: `${runtime.notes ? `${runtime.notes} ` : ""}This configured path is not the release-certified execution mode.`,
23
+ certification: runtime.certification ?? "adapter-tested",
24
+ notes: `${runtime.notes ? `${runtime.notes} ` : ""}This wrapper is maintained by Bivy; its current configured path is adapter-tested rather than release-tested.`,
24
25
  };
25
26
  }
26
27
  return { ...runtime, certification: "release-tested", testedVersion: entry.pinnedVersion };
@@ -34,7 +34,7 @@ export function linearBranchName(identifier) {
34
34
  const slug = identifier.toLowerCase().replace(/[^a-z0-9._-]+/g, "-").replace(/^-+|-+$/g, "");
35
35
  return `bivy/linear-${slug || "issue"}`;
36
36
  }
37
- export function buildLinearTaskPrompt(issue) {
37
+ export function buildLinearTaskPrompt(issue, instructions) {
38
38
  return [
39
39
  `You are working on Linear issue ${issue.identifier}: ${issue.title}`,
40
40
  "",
@@ -42,8 +42,8 @@ export function buildLinearTaskPrompt(issue) {
42
42
  "",
43
43
  issue.url ? `Issue: ${issue.url}` : "",
44
44
  "",
45
- "Understand the issue and surrounding codebase, implement it completely, and run the project's tests, linter, and type-checker.",
45
+ instructions?.trim() || "Understand the issue and surrounding codebase, implement it completely, and run the project's tests, linter, and type-checker.",
46
46
  "",
47
- `When finished, commit and push your changes and open a pull request yourself. Include a link to ${issue.identifier} in the pull request description.`,
47
+ instructions?.trim() ? "" : `When finished, commit and push your changes and open a pull request yourself. Include a link to ${issue.identifier} in the pull request description.`,
48
48
  ].filter((part, index, all) => part !== "" || all[index - 1] !== "").join("\n");
49
49
  }
package/dist/metadata.js CHANGED
@@ -103,7 +103,14 @@ export class MetadataStore {
103
103
  const prev = this.data.sessions[input.id];
104
104
  const createdAt = input.createdAt ?? prev?.createdAt ?? nowIso();
105
105
  const updatedAt = input.updatedAt ?? nowIso();
106
- this.data.sessions[input.id] = { ...prev, ...input, createdAt, updatedAt };
106
+ // Runtime adapters do not all expose the same resume fields. In particular,
107
+ // an adapter can temporarily have no sessionFile while it is being rebuilt.
108
+ // Do not let that partial snapshot erase the durable resume ref (or any
109
+ // other field) already recorded for the session. The old object spread did
110
+ // exactly that: `{ path: undefined }` replaced the Pi transcript path, so
111
+ // reopening the saved row fell through to a fresh session.
112
+ const defined = Object.fromEntries(Object.entries(input).filter(([, value]) => value !== undefined));
113
+ this.data.sessions[input.id] = { ...prev, ...defined, createdAt, updatedAt };
107
114
  this.save();
108
115
  }
109
116
  touchSession(id, status) {
@@ -277,8 +277,8 @@ export function cliAgentManifest() {
277
277
  label: spec.displayName,
278
278
  command: spec.command,
279
279
  hidden: Boolean(spec.hidden),
280
- supportTier: spec.supportTier ?? "beta",
281
- certification: spec.testedVersion ? "release-tested" : (spec.supportTier ?? "beta") === "beta" ? "adapter-tested" : "unverified",
280
+ supportTier: spec.supportTier ?? "supported",
281
+ certification: spec.testedVersion ? "release-tested" : (spec.supportTier ?? "supported") === "supported" || (spec.supportTier ?? "supported") === "beta" ? "adapter-tested" : "unverified",
282
282
  ...(spec.testedVersion ? { testedVersion: spec.testedVersion } : {}),
283
283
  headlessFlags: [...headless].filter((a) => !a.includes("{")),
284
284
  install: spec.install ?? null,
@@ -1015,7 +1015,7 @@ function runtimeCertification(runtime) {
1015
1015
  };
1016
1016
  if (runtime.testedVersion)
1017
1017
  return { certification: "release-tested", testedVersion: runtime.testedVersion };
1018
- return { certification: runtime.supportTier === "beta" ? "adapter-tested" : "unverified" };
1018
+ return { certification: runtime.supportTier === "supported" || runtime.supportTier === "beta" ? "adapter-tested" : "unverified" };
1019
1019
  }
1020
1020
  function runtimeProtection(runtime) {
1021
1021
  // Native SDK/CLI sandboxes receive the requested read-only/workspace/full tier
package/dist/server.js CHANGED
@@ -4212,7 +4212,7 @@ async function runIssueTaskInner(cfg, issue, source, overrides = {}) {
4212
4212
  try {
4213
4213
  recordRunAuditCorrelation(record, overrides.correlation);
4214
4214
  emit(record, "started", `Started work on ${cfg.owner}/${cfg.repo}#${issue.number}.`);
4215
- await runSessionTurn(record, buildTaskPrompt(issue, nodeGithubIssuePrompt()), overrides.signal);
4215
+ await runSessionTurn(record, buildTaskPrompt(issue, overrides.instructions ?? nodeGithubIssuePrompt()), overrides.signal);
4216
4216
  if (overrides.signal?.aborted)
4217
4217
  throw overrides.signal.reason ?? new Error("Run cancelled");
4218
4218
  emit(record, "agent_done", `Agent finished issue #${issue.number}; running deterministic checks.`);
@@ -4918,6 +4918,7 @@ async function runWorkItem(item, report, signal) {
4918
4918
  sandbox: normalizeSandboxTier(item.sandbox),
4919
4919
  approvalMode: approvalModeFrom(item.approvalMode),
4920
4920
  onEvidence: report,
4921
+ instructions: item.body,
4921
4922
  signal,
4922
4923
  correlation: { runId: item.id, attempt: item.attempt ?? 1, machineId: identity.nodeId },
4923
4924
  });
@@ -4938,7 +4939,7 @@ async function runWorkItem(item, report, signal) {
4938
4939
  throw new Error(`Linear work item has an invalid repo "${repoSlug}"`);
4939
4940
  // Case B: a re-dispatch the control plane correlated to an existing session
4940
4941
  // continues it as a normal chat instead of starting cold (mirrors GitHub).
4941
- if (await continueCorrelatedSession(item, buildLinearTaskPrompt(issue), report, { resumeOnMissing: item.targetKind === "existing_session", signal }))
4942
+ if (await continueCorrelatedSession(item, buildLinearTaskPrompt(issue, item.body), report, { resumeOnMissing: item.targetKind === "existing_session", signal }))
4942
4943
  return;
4943
4944
  const githubToken = await resolveGitHubToken();
4944
4945
  if (!githubToken)
@@ -4965,7 +4966,7 @@ async function runWorkItem(item, report, signal) {
4965
4966
  catch { }
4966
4967
  }
4967
4968
  await report({ output: { sessionId: record.id, branch }, events: [{ at: new Date().toISOString(), kind: "branch", summary: "Linear issue working branch and session created.", ref: branch, url: issue.url }] });
4968
- await runSessionTurn(record, buildLinearTaskPrompt(issue), signal);
4969
+ await runSessionTurn(record, buildLinearTaskPrompt(issue, item.body), signal);
4969
4970
  if (signal.aborted)
4970
4971
  throw signal.reason ?? new Error("Run cancelled");
4971
4972
  await branchPublish.maybePushWorktreeBranch(record);
@@ -104,7 +104,7 @@ export function resolveSessionContract(input) {
104
104
  };
105
105
  const supportTier = input.supportTier ?? "experimental";
106
106
  const hasProtectionDegradation = degradedReasons.some((r) => PROTECTION_AREAS.has(r.area));
107
- const requiresAcknowledgement = supportTier === "supported" && hasProtectionDegradation && !input.acknowledgedAt;
107
+ const requiresAcknowledgement = input.certification === "release-tested" && hasProtectionDegradation && !input.acknowledgedAt;
108
108
  return {
109
109
  schemaVersion: SESSION_CONTRACT_SCHEMA_VERSION,
110
110
  resolvedAt: input.now,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bivy/bivy",
3
- "version": "0.15.0-staging.2",
3
+ "version": "0.15.0-staging.20",
4
4
  "type": "module",
5
5
  "license": "AGPL-3.0-only",
6
6
  "description": "Run coding agents on machines you own. Open-source, self-hostable agent workspace.",
@@ -66,6 +66,6 @@
66
66
  "nanoid": "3.3.18",
67
67
  "undici": "8.10.0"
68
68
  },
69
- "readme": "# Bivy\n\n[![npm](https://img.shields.io/npm/v/@bivy/bivy?color=2b6cb0&label=%40bivy%2Fbivy)](https://www.npmjs.com/package/@bivy/bivy)\n[![license: AGPL-3.0-only](https://img.shields.io/badge/license-AGPL--3.0--only-2b6cb0)](LICENSE)\n[![node](https://img.shields.io/badge/node-%E2%89%A520-2b6cb0)](https://nodejs.org)\n\n**Run coding agents on the machines you already own — then reach them from your\nphone, browser, or another terminal.**\n\nStart Claude Code on your workstation, right where the repo, the running dev\nserver, and the staging database already live. Walk away. On the train, open\nyour phone: read what the agent did, answer its question, approve the migration\n— over a link only your devices can decrypt. The work never left your machine.\n\n```bash\ncurl -fsSL https://bivy.sh/install.sh | bash # install + guided setup\ncd your-repo\nbivy run claude # start an agent where your work lives\nbivy open # pick it up from your phone or browser\n```\n\nFirst thing to try: ask the agent to explain the repository, make one small safe\nchange, then open the same Session in the web app or on your phone while it runs.\n\n**[Quickstart](docs/quickstart.md)** ·\n**[Docs](docs/README.md)** ·\n**[Why Bivy](docs/why-bivy.md)** ·\n**[Security model](docs/security-model.md)** ·\n**[bivy.sh](https://bivy.sh)**\n\n> **0.x software.** The core loop is solid and used daily. Interfaces and\n> cross-runtime fidelity still change between releases — check the\n> [runtime support matrix](docs/runtime-support-matrix.md) before you depend on\n> a specific agent capability.\n\n## Why not just a cloud sandbox?\n\nA hosted sandbox starts from an *approximation* of your environment. A Bivy\nMachine **is** your environment — the actual working tree, the services already\nrunning, the caches already warm.\n\n| | Cloud sandbox | Bivy Machine |\n|---|---|---|\n| Your repository | a cloned copy | the real working tree, uncommitted changes and all |\n| Dev server & database | mocked, or absent | already running, right beside the agent |\n| Private networks & internal APIs | out of reach | reachable |\n| Toolchains, package caches | cold, reinstalled each time | warm, already installed |\n| GPUs / local inference | rented separately | the ones on your box |\n| Where your code sits | someone else's infrastructure | the machine you already trust |\n\nYou keep the environment. Bivy adds the part that was missing: **reaching that\nenvironment from anywhere, and leaving it working while you're gone.**\n\n## What you can do\n\nBivy gives you two ways to put an agent to work.\n\n### Sessions — interactive, and portable\n\nStart an agent, watch it work, jump in to steer, stop, or approve. Then leave\nyour desk and keep going:\n\n```bash\nbivy run claude # or codex, pi, gemini, and a dozen more\nbivy open # continue the same session in the browser or PWA\nbivy resume # pick it back up in the terminal\nbivy run claude --no-follow # start it in the background instead of attaching\nbivy run claude --chat # start the governed app session and open it in the browser\n```\n\n- **Reconnect from anywhere** — phone, browser, or another terminal — to the\n same live Session. The PWA adds voice input, read-aloud, phone-to-agent\n file/image uploads, and agent-to-phone attachments.\n- **Move work without starting over.** Import existing Claude Code and Codex\n Sessions, or fork, copy, and move a Bivy Session to another agent, model, or\n Machine.\n- **Run more than one Machine** — a workstation, a private-network server, a GPU\n box — on one account, and pick the environment each Session needs.\n\n### Runs — unattended, and accountable\n\nQueue one on demand, or let an event kick it off — either way it returns\nimmediately and reports back:\n\n```bash\nbivy runs start \"...\" # queue a one-off unattended Run, then `bivy runs wait <id>`\nbivy automation init # or define governed jobs in .bivy/automations.yaml\n```\n\n- **Trigger from real events** — a failed CI job, a GitHub or Linear issue,\n Slack, a schedule, or a signed webhook.\n- **Pin the guardrails** — Machine, agent, model, sandbox, approval mode, and a\n hard attempt ceiling — right next to the job.\n- **Get a Receipt** — every Run reports the checks it ran and how it turned out,\n not just a wall of output.\n\nTry the [capability recipes](docs/capability-recipes.md) to see each of these\nend to end, or the [runtime support matrix](docs/runtime-support-matrix.md) for\nexactly what each agent supports.\n\n## Bring your own stack\n\nUse provider subscriptions through native agent logins, API keys stored in\nBivy's vault, or local / OpenAI-compatible inference. Claude Code, Codex, and Pi\nhave first-class SDK integrations; any other ACP or headless agent needs no\nadapter at all — it's a data row you add with one command:\n\n```bash\nbivy agent add # register an existing ACP or process agent\n```\n\n## Install\n\n```bash\ncurl -fsSL https://bivy.sh/install.sh | bash\n```\n\nmacOS and Linux. Requires Node.js 20 or newer. The installer puts the\n[`@bivy/bivy`](https://www.npmjs.com/package/@bivy/bivy) npm package and the\n`bivy` command on your `PATH` with optional bridges skipped for speed, then runs the guided `bivy setup` wizard — agent\nchoice, selected-agent install if needed, remote access, and an auto-start background service (launchd on macOS,\nsystemd on Linux). Re-running it on a machine that already has Bivy just applies\nthe latest build and restarts the service.\n\n**What needs an account, and what doesn't.** The CLI alone — `bivy run`,\n`bivy resume`, `bivy sessions` — needs no account and no server; `bivy setup`\nlets you pick **local only for now** and skip remote access. A browser or phone\nUI needs a control plane, because the node hosts none: use the hosted one at\n`app.bivy.sh` (sign in with GitHub or email; free tier plus a paid plan — see\n[bivy.sh#pricing](https://bivy.sh#pricing)) or\n[self-host your own](docs/self-host-quickstart.md). Switch any time with\n`bivy relay:setup`.\n\n**What the installer does with sudo.** It escalates only when it must, and\ntells you when it does:\n\n- Debian/Ubuntu without a suitable Node.js: `sudo apt-get install build-essential\n python3 curl`, then NodeSource's Node 22 setup script via `sudo`.\n- Other Linux, or macOS, without a suitable Node.js: downloads the official\n Node 22 tarball from nodejs.org (sha256-checked) and installs it under\n `/usr/local` with `sudo`.\n- If npm's global prefix isn't writable it falls back to `~/.local` — it never\n runs `npm install` under `sudo`.\n- It appends a marked PATH block to `~/.bashrc` or `~/.zshrc`\n (`BIVY_NO_RC_UPDATE=1` to opt out).\n\nWant no sudo at all? Bring your own Node.js 20+ and skip the script:\n\n```bash\nnpm install -g @bivy/bivy && bivy setup # install globally\nnpx @bivy/bivy setup # or try it once, no install\n```\n\nReleases are published from CI with provenance attestations; verify a build's\norigin with `npm audit signatures`. See [`docs/releasing.md`](docs/releasing.md).\n\n### Your first session\n\nOnce `bivy setup` finishes, use Bivy from inside an existing repo. The first win\nis simple: start the local agent, give it a real task in that environment, then\nreopen the same Session from another surface.\n\n```bash\ncd your-repo\nbivy run claude # start an agent as a durable session in the current repo\n# Try: \"Explain this repo and suggest one small, safe improvement.\"\nbivy open # open that same session in the web app (needs relay setup)\nbivy resume # or pick it back up here in the terminal\n```\n\nFrom here the [quickstart](docs/quickstart.md) walks through Runs, multiple\nMachines, and automations.\n\n### Install options\n\nEnvironment variables passed to the one-line installer change what it does:\n\n| Goal | Variable |\n|---|---|\n| Track the dev channel (new build on every merge to `main`) | `BIVY_CHANNEL=staging` |\n| Pin an exact version | `BIVY_VERSION=0.1.0` |\n| Install the npm package into a user-owned prefix | `BIVY_NPM_PREFIX=~/.local` |\n| Preinstall every known upstream agent | `BIVY_INSTALL_ALL_AGENTS=1` |\n| Install optional Bivy bridges/native terminal dependency up front | `BIVY_INSTALL_OPTIONAL_DEPS=1` |\n| Don't touch `~/.bashrc` / `~/.zshrc`; print the PATH line instead | `BIVY_NO_RC_UPDATE=1` |\n\nFor example: `BIVY_CHANNEL=staging curl -fsSL https://bivy.sh/install.sh | bash`.\n\nWorking from a checkout of this repository instead:\n\n```bash\npnpm install\npnpm run setup\n```\n\nSee [`docs/install.md`](docs/install.md) for where data lives, service\nmanagement, and uninstall.\n\n## Updating\n\n```bash\nbivy update\n```\n\n`bivy update` detects how Bivy was installed and does the right thing, then\nwaits for any active session to finish its current turn and restarts the\nbackground service so the node reconnects on the new build:\n\n| Install kind | What `bivy update` does |\n|---|---|\n| npm global (`npm i -g`) | `npm install -g @bivy/bivy@<channel>`, then restart the service |\n| installer / packaged | re-runs `install.sh` (migrating to npm if needed), then restart |\n| git checkout | `git pull --ff-only` + `pnpm install --frozen-lockfile`, then restart |\n| `npx` run | nothing to update — each run already fetches the latest |\n\nUpdates follow the release **channel** recorded at install time — `latest`\n(production) by default, or `staging` if you installed with\n`BIVY_CHANNEL=staging`. Switch channels (the choice is remembered for next\ntime), or skip the wait for a busy session:\n\n```bash\nbivy update --staging # move to the dev channel\nbivy update --stable # move back to production (latest)\nbivy update --force # don't wait for an in-flight turn to finish\n```\n\nThe daemon also checks the registry periodically and posts an in-session notice\nwhen a newer build is available.\n\n## Architecture\n\nBivy has three parts. **Only the first one holds your data.**\n\n```text\n your machine hosted or self-hosted\n\n ┌──────────────┐ ┌─────────┐ ┌───────────────┐\n │ node daemon │ ──dials──▶ │ relay │ ◀────▶ │ control plane │\n │ agents, keys │ outbound │ opaque │ │ accounts, web │\n │ repo, tools │ │ frames │ │ app, metadata │\n └──────────────┘ └─────────┘ └───────────────┘\n ▲ ▲\n └────────── end-to-end encrypted session ───────────┘\n phone · browser · another terminal\n```\n\n- **Node** — a daemon on your machine. Owns the workspace, credentials, and agent\n processes. Serves an API and WebSocket on `http://localhost:4317` plus a\n `/healthz` probe. **It hosts no web UI.**\n- **Relay** — forwards encrypted frames between your node and your devices. Your\n node dials out, so no inbound port is opened. The relay cannot read the frames.\n- **Control plane** — holds your account, node registry, and session index, and\n serves the web/PWA client. Use the hosted one or run your own.\n\nBecause the node serves no UI, a browser or phone needs a control plane — hosted\nat `app.bivy.sh`, or one you deploy yourself. The terminal CLI needs neither.\nInteractive Session traffic is end-to-end encrypted between a Machine and its\npaired devices: the relay never sees plaintext and cannot decrypt it. Who can\n*authorize* a device depends on how you pair — with a QR / `bivy link` pairing,\nor on a self-hosted deployment, the control plane can't read your Sessions\neither; with hosted account sign-in you trust the control plane to authorize\ndevices and to serve the web app that holds the keys. See\n[known limitations](docs/security-model.md#known-limitations-for-0x).\n\nSee [`docs/remote-access.md`](docs/remote-access.md) and\n[`docs/security-model.md`](docs/security-model.md).\n\n## Supported agents\n\n**Claude Code and Codex are the recommended, release-certified paths.** The\nbroader catalog stays available under **More agents**; capabilities and fidelity\nvary by runtime.\n\n| Agent | Command | Notes |\n|---|---|---|\n| Claude Code | `bivy run claude` | Uses the operator-installed `claude` command through an SDK bridge |\n| Codex | `bivy run codex` | Installs `@openai/codex` |\n| Pi | `bivy run pi` | Uses the operator-installed `pi` command and Pi auth/config |\n| OpenCode | `bivy run opencode` | Installs `opencode-ai` |\n| Gemini CLI | `bivy run gemini` | Installs `@google/gemini-cli` |\n| Qwen Code | `bivy run qwen` | Installs `@qwen-code/qwen-code` |\n| Goose | `bivy run goose` | Requires `goose` on PATH |\n| Aider | `bivy run aider` | No session resume (upstream gap) |\n| Cline | `bivy run cline` | Installs `cline` |\n| Crush | `bivy run crush` | No session resume (upstream gap) |\n| Cursor | `bivy run cursor` | ACP-capable |\n| GitHub Copilot | `bivy run copilot` | ACP-capable |\n| Grok | `bivy run grok` | Model selection |\n| Amp | `bivy run amp` | Native thread resume |\n| Auggie | `bivy run auggie` | Headless CLI |\n| Droid | `bivy run droid` | Model selection |\n| Continue | `bivy run continue` | Headless CLI |\n| Kilo Code | `bivy run kilocode` | ACP-capable |\n| Rovo Dev | `bivy run rovodev` | Installed out of band |\n\nAlso defined but hidden from the picker as *Experimental* — runnable via\n`BIVY_RUNTIME=<id>`: Codebuff (`codebuff`, no verified headless mode upstream\nyet), Hermes (`hermes`, generic process adapter), and OpenClaw (`openclaw`,\nCLI adapter only, no resume yet).\n\nAny other command works via `bivy run -- ./your-agent --flags`. ACP-capable\nagents can be promoted to Bivy's governed protocol path for per-tool approvals\nand native resume. To add a reusable process or ACP agent to both the CLI and web\npicker without changing Bivy, run `bivy agent add`, or scaffold and install a\ndeclarative [plugin manifest](docs/plugins.md) with `bivy plugin init`\n(declarative plugins are Experimental, `v1alpha1`, and run out of process).\n\n[`docs/runtime-support-matrix.md`](docs/runtime-support-matrix.md) lists exactly\nwhat each agent supports — resume, model selection, approvals, sandboxing.\n\n## Common commands\n\n```bash\nbivy # show the command overview\nbivy run claude # launch Claude Code as a durable session\nbivy run codex # run a different agent\nbivy sessions # list live and saved sessions\nbivy resume # resume the most recent session\nbivy open # open the web app (requires relay setup)\nbivy automation init # create .bivy/automations.yaml\nbivy agent add # connect an existing ACP or process agent\nbivy plugin list # installed declarative integration packages\nbivy status # config summary and node reachability\nbivy doctor # health check\nbivy logs -f # tail node logs\nbivy update # update Bivy and restart the service\n```\n\nFull command list, flags, and examples: [`docs/cli-reference.md`](docs/cli-reference.md).\n\n## Configuration\n\nThe common knobs:\n\n```bash\nBIVY_WORKSPACE=/path/to/repo # default workspace\nBIVY_SANDBOX=read-only # read-only | workspace-write (default) | danger-full-access\nBIVY_APPROVAL_MODE=risky # never | risky | always | autonomous (default)\n```\n\nCreate and inspect the typed node configuration, or add repository-owned\nsafety/check/retry policy:\n\n```bash\nbivy config init\nbivy config set defaults.agent codex\nbivy config explain defaults.sandbox\nbivy config init --project # .bivy/policy.yaml\n```\n\nSee [`docs/config-as-code.md`](docs/config-as-code.md). Every environment\nvariable and precedence rule lives in\n[`docs/configuration.md`](docs/configuration.md).\n\n## Approvals and sandboxing\n\nThe default approval mode is **`autonomous`**: agents act without per-action\nprompts. How much that actually protects you depends on the runtime. Native-sandbox\nagents enforce the chosen access tier; structured runtimes also pass tool calls\nthrough Bivy's policy and approval layer. Process agents that Bivy cannot\nintercept run with your OS user permissions — the picker flags this and requires\nconfirmation before you pick that path.\n\nWhere Bivy receives structured shell/file calls, a heuristic floor blocks known\ncatastrophic commands and structured writes outside the workspace, and a\nbackstop set (force-push, publish, deploy, sudo) pauses for a human. This catches\naccidents; **it is not an adversarial isolation boundary.**\n\nWant to be asked about more? Set the mode explicitly:\n\n```bash\nBIVY_APPROVAL_MODE=risky # prompt on risky shell commands and file edits\nBIVY_APPROVAL_MODE=always # prompt on all shell commands and file edits\nBIVY_APPROVAL_MODE=never # no prompts; structured-tool heuristic blocks still apply where available\n```\n\nApprove from the terminal, browser, or phone.\n\nSandbox tiers (`read-only`, `workspace-write`, `danger-full-access`) are enforced\nnatively by agents that support them — Codex, Claude Code, Gemini CLI, Qwen Code.\nAgents without a native sandbox may expose structured tool or MCP controls, but\nthose don't cover activity the agent performs outside those channels; some\nprocess adapters run entirely with your user permissions. Check the picker's\nProtection label. **Bivy does not currently ship its own OS-level jail.**\n\n## Credentials\n\nInteractive prompts, transcripts, and workspace files stay encrypted across the\nrelay. Credentials can remain on a Machine or in a vault you control:\n\n```bash\nbivy secrets list\nbivy secrets set github.repo-token\nbivy secrets ref github.repo-token op://Bivy/GitHub/repo-token\nbivy secrets doctor\n```\n\n`secret://`, `env://`, and `op://` (1Password) references resolve on demand when\nthe daemon provisions an agent run, so raw values never sit in your config.\n\n**One deliberate exception to relay blindness:** if you explicitly enable hosted\nunattended provisioning, Bivy Cloud may store encrypted cloud, repository,\nmodel, or key-escrow material that the service can technically access. Treat this\nas an explicit hosted-custody mode. See the\n[security model](docs/security-model.md#what-the-control-plane-sees) and\n[`docs/key-management.md`](docs/key-management.md).\n\n## Automations as code\n\nDefine governed jobs in `.bivy/automations.yaml`, validate them, and simulate\ntrigger events locally before applying anything:\n\n```bash\nbivy automation init\nbivy automation validate\nbivy automation test --event .bivy/events/failed-ci.yaml\nbivy automation apply\n```\n\nInstructions are encrypted on the applying node before upload. Safety policy\nlives beside the job — sandbox, approval mode, and a hard attempt ceiling that\nretry/fallback rules cannot exceed. See\n[`docs/automations-as-code.md`](docs/automations-as-code.md).\n\n## GitHub Runs\n\nLabel an issue `bivy` (or `bivy/<machine>` to target a Machine), or mention the\nBivy GitHub App in a comment. Bivy creates a Run on the selected Machine, uses an\nisolated worktree, executes configured checks, and reports an explicit outcome.\n\nCore applies no commercial usage limits. Bivy Cloud billing and commercial\npolicy live in the separate Cloud repository.\n\nA private GitHub App only installs on the account that owns it, so connect one\napp per GitHub account — one for your personal repos, one per organization\n(`bivy github:app-create --org <org>`). A node can serve several at once, each\nwith its own key and `@`-mention handle.\n\nSee [`docs/github-work-queue.md`](docs/github-work-queue.md).\n\n## Linear Runs\n\nApply `bivy` or `bivy/<machine>` to a Linear issue to create a Run on the selected\nMachine. The Machine fetches issue content directly from Linear, works in an\nisolated GitHub worktree, and asks the agent to open a pull request. See\n[`docs/linear-work-queue.md`](docs/linear-work-queue.md).\n\n## Development\n\n```bash\npnpm install\npnpm run dev # node daemon on http://localhost:4317\npnpm run dev:web # web client dev server (proxies /api and /ws to the node)\n```\n\nChecks — all of these run in CI:\n\n```bash\npnpm run typecheck\npnpm run typecheck:web\npnpm run lint\npnpm run test:unit\npnpm run test:core\npnpm run check:licenses\npnpm run check:secrets\n```\n\nRepository layout:\n\n- `src/` — node daemon, runtime adapters, approvals, secrets, sessions\n- `bin/` — the `bivy` CLI\n- `packages/core` — shared protocol, pairing, wire format\n- `packages/web` — the React/Vite PWA client (`@bivy/web`)\n- `services/relay` — self-hostable relay\n- `services/control-plane` — self-hostable control plane\n- `deploy/` — self-host deployment examples\n\nSee [`CONTRIBUTING.md`](CONTRIBUTING.md).\n\n## Self-hosting\n\nNode, relay, and control plane are all in this repository. Point a node at your\nown deployment by passing URLs to `bivy relay:setup` — re-running it switches an\nexisting node over to the new endpoints:\n\n```bash\nbivy relay:setup \\\n --control-plane https://bivy.example.com \\\n --relay wss://relay.example.com\n```\n\nEach URL has a flag and an environment-variable equivalent (the flag wins):\n\n| Flag | Environment variable | Points at | Default |\n|---|---|---|---|\n| `--control-plane <url>` | `BIVY_CONTROL_PLANE_URL` | accounts, node registry, and the web-app API | hosted (`app.bivy.sh`) |\n| `--relay <wss-url>` | `BIVY_RELAY_URL` | the encrypted-frame relay your node dials out to | hosted |\n| `--client <url>` | `BIVY_CLIENT_BASE_URL` | base URL used when building app/PWA links | the `--control-plane` URL |\n\nSign-in defaults to GitHub device login (`--github`); pass\n`--email you@example.com` for an email magic-link, or `--session-token <token>`\nto skip interactive sign-in. `relay:setup` checks the control plane is reachable,\nenrolls this node, and writes the endpoints to `.bivy/relay.json`, so `bivy open`,\n`bivy link`, and `bivy update` all keep using your deployment afterwards.\n\n**Self-hosting is community-supported** — no SLA, best-effort help via GitHub\nissues. You own TLS, backups, upgrades, and hardening. Start with the\none-command VPS path in\n[`docs/self-host-quickstart.md`](docs/self-host-quickstart.md); the ops\nreference (backups, rotation, security boundary) is\n[`docs/self-host.md`](docs/self-host.md).\n\n## Security\n\nReport vulnerabilities through [GitHub private vulnerability reporting](https://github.com/bivysh/bivy/security/advisories/new).\nPlease don't open a public issue. See [`SECURITY.md`](SECURITY.md) for scope,\nresponse times, and safe harbour, and [`docs/security-model.md`](docs/security-model.md)\nfor the trust model and known limitations.\n\n## License\n\nBivy Core is free and open-source software under the GNU Affero General Public\nLicense, version 3.0 only (AGPL-3.0-only). You may use, study, modify, and\nself-host it under that license. If you modify Bivy and let users interact with\nit over a network, section 13 requires you to offer them the corresponding\nsource code. See [`LICENSE`](LICENSE).\n\n**Where the open-core line is.** Everything in this repository — node, CLI,\nrelay, control plane, and the web/PWA client — is AGPL Core, with no usage\nlimits. **Bivy Cloud** is the hosted operation of that stack plus billing and\nplans, and lives in a separate private repository. Contributions are accepted\nunder the [DCO](CONTRIBUTING.md#certificate-of-origin); there is no CLA.\n",
69
+ "readme": "# Bivy\n\n[![npm](https://img.shields.io/npm/v/@bivy/bivy?color=2b6cb0&label=%40bivy%2Fbivy)](https://www.npmjs.com/package/@bivy/bivy)\n[![license: AGPL-3.0-only](https://img.shields.io/badge/license-AGPL--3.0--only-2b6cb0)](LICENSE)\n[![node](https://img.shields.io/badge/node-%E2%89%A520-2b6cb0)](https://nodejs.org)\n\n**Run coding agents on the machines you already own — then reach them from your\nphone, browser, or another terminal.**\n\nStart Claude Code on your workstation, right where the repo, the running dev\nserver, and the staging database already live. Walk away. On the train, open\nyour phone: read what the agent did, answer its question, approve the migration\n— over a link only your devices can decrypt. The work never left your machine.\n\n```bash\ncurl -fsSL https://bivy.sh/install.sh | bash # install + guided setup\ncd your-repo\nbivy run claude # start an agent where your work lives\nbivy open # pick it up from your phone or browser\n```\n\nFirst thing to try: ask the agent to explain the repository, make one small safe\nchange, then open the same Session in the web app or on your phone while it runs.\n\n**[Quickstart](docs/quickstart.md)** ·\n**[Docs](docs/README.md)** ·\n**[Why Bivy](docs/why-bivy.md)** ·\n**[Security model](docs/security-model.md)** ·\n**[bivy.sh](https://bivy.sh)**\n\n> **0.x software.** The core loop is solid and used daily. Interfaces and\n> cross-runtime fidelity still change between releases — check the\n> [runtime support matrix](docs/runtime-support-matrix.md) before you depend on\n> a specific agent capability.\n\n## Why not just a cloud sandbox?\n\nA hosted sandbox starts from an *approximation* of your environment. A Bivy\nMachine **is** your environment — the actual working tree, the services already\nrunning, the caches already warm.\n\n| | Cloud sandbox | Bivy Machine |\n|---|---|---|\n| Your repository | a cloned copy | the real working tree, uncommitted changes and all |\n| Dev server & database | mocked, or absent | already running, right beside the agent |\n| Private networks & internal APIs | out of reach | reachable |\n| Toolchains, package caches | cold, reinstalled each time | warm, already installed |\n| GPUs / local inference | rented separately | the ones on your box |\n| Where your code sits | someone else's infrastructure | the machine you already trust |\n\nYou keep the environment. Bivy adds the part that was missing: **reaching that\nenvironment from anywhere, and leaving it working while you're gone.**\n\n## What you can do\n\nBivy gives you two ways to put an agent to work.\n\n### Sessions — interactive, and portable\n\nStart an agent, watch it work, jump in to steer, stop, or approve. Then leave\nyour desk and keep going:\n\n```bash\nbivy run claude # or codex, pi, gemini, and a dozen more\nbivy open # continue the same session in the browser or PWA\nbivy resume # pick it back up in the terminal\nbivy run claude --no-follow # start it in the background instead of attaching\nbivy run claude --chat # start the governed app session and open it in the browser\n```\n\n- **Reconnect from anywhere** — phone, browser, or another terminal — to the\n same live Session. The PWA adds voice input, read-aloud, phone-to-agent\n file/image uploads, and agent-to-phone attachments.\n- **Move work without starting over.** Import existing Claude Code and Codex\n Sessions, or fork, copy, and move a Bivy Session to another agent, model, or\n Machine.\n- **Run more than one Machine** — a workstation, a private-network server, a GPU\n box — on one account, and pick the environment each Session needs.\n\n### Runs — unattended, and accountable\n\nQueue one on demand, or let an event kick it off — either way it returns\nimmediately and reports back:\n\n```bash\nbivy runs start \"...\" # queue a one-off unattended Run, then `bivy runs wait <id>`\nbivy automation init # or define governed jobs in .bivy/automations.yaml\n```\n\n- **Trigger from real events** — a failed CI job, a GitHub or Linear issue,\n Slack, a schedule, or a signed webhook.\n- **Pin the guardrails** — Machine, agent, model, sandbox, approval mode, and a\n hard attempt ceiling — right next to the job.\n- **Get a Receipt** — every Run reports the checks it ran and how it turned out,\n not just a wall of output.\n\nTry the [capability recipes](docs/capability-recipes.md) to see each of these\nend to end, or the [runtime support matrix](docs/runtime-support-matrix.md) for\nexactly what each agent supports.\n\n## Bring your own stack\n\nUse provider subscriptions through native agent logins, API keys stored in\nBivy's vault, or local / OpenAI-compatible inference. Claude Code, Codex, and Pi\nhave first-class SDK integrations; any other ACP or headless agent needs no\nadapter at all — it's a data row you add with one command:\n\n```bash\nbivy agent add # register an existing ACP or process agent\n```\n\n## Install\n\n```bash\ncurl -fsSL https://bivy.sh/install.sh | bash\n```\n\nmacOS and Linux. Requires Node.js 20 or newer. The installer puts the\n[`@bivy/bivy`](https://www.npmjs.com/package/@bivy/bivy) npm package and the\n`bivy` command on your `PATH` with optional bridges skipped for speed, then runs\n`bivy setup` — agent choice, selected-agent install if needed, remote access,\nand an auto-start background service (launchd on macOS, systemd on Linux).\n\nIf Claude Code, Codex, OpenCode, Gemini, or another agent is already installed,\nsetup says so and uses that existing CLI, login, and configuration. Bivy does\nnot replace the agent or copy its native credentials; it adds remote continuity\nand governance around the agent you already use. Re-running the installer on a\nmachine that already has Bivy just applies the latest build and restarts the\nservice.\n\n**What needs an account, and what doesn't.** The CLI alone — `bivy run`,\n`bivy resume`, `bivy sessions` — needs no account and no server; `bivy setup`\nlets you pick **local only for now** and skip remote access. A browser or phone\nUI needs a control plane, because the node hosts none: use the hosted one at\n`app.bivy.sh` (sign in with GitHub or email; free tier plus a paid plan — see\n[bivy.sh#pricing](https://bivy.sh#pricing)) or\n[self-host your own](docs/self-host-quickstart.md). Switch any time with\n`bivy relay:setup`.\n\nPrefer to inspect the installer first?\n\n```bash\ncurl -fsSL https://bivy.sh/install.sh -o install.sh\nless install.sh\nbash install.sh\n```\n\n**What the installer does with sudo.** It escalates only when it must, and\ntells you when it does:\n\n- Debian/Ubuntu without a suitable Node.js: `sudo apt-get install curl\n ca-certificates`, then NodeSource's Node 22 setup script via `sudo`.\n- Other Linux, or macOS, without a suitable Node.js: downloads the official\n Node 22 tarball from nodejs.org (sha256-checked) and installs it under\n `/usr/local` with `sudo`.\n- If npm's global prefix isn't writable it falls back to `~/.local` — it never\n runs `npm install` under `sudo`.\n- It appends a marked PATH block to `~/.bashrc` or `~/.zshrc`\n (`BIVY_NO_RC_UPDATE=1` to opt out).\n\nWant no sudo at all? Bring your own Node.js 20+ and skip the script:\n\n```bash\nnpm install -g @bivy/bivy && bivy setup # install globally\nnpx @bivy/bivy setup # or try it once, no install\n```\n\nReleases are published from CI with provenance attestations; verify a build's\norigin with `npm audit signatures`. See [`docs/releasing.md`](docs/releasing.md).\n\n### Your first session\n\nOnce `bivy setup` finishes, use Bivy from inside an existing repo. The first win\nis simple: start the local agent, give it a real task in that environment, then\nreopen the same Session from another surface.\n\n```bash\ncd your-repo\nbivy run claude # start an agent as a durable session in the current repo\n# Try: \"Explain this repo and suggest one small, safe improvement.\"\nbivy open # open that same session in the web app (needs relay setup)\nbivy resume # or pick it back up here in the terminal\n```\n\nFrom here the [quickstart](docs/quickstart.md) walks through Runs, multiple\nMachines, and automations.\n\n### Install options\n\nEnvironment variables passed to the one-line installer change what it does:\n\n| Goal | Variable |\n|---|---|\n| Track the dev channel (new build on every merge to `main`) | `BIVY_CHANNEL=staging` |\n| Pin an exact version | `BIVY_VERSION=0.1.0` |\n| Install the npm package into a user-owned prefix | `BIVY_NPM_PREFIX=~/.local` |\n| Preinstall every known upstream agent | `BIVY_INSTALL_ALL_AGENTS=1` |\n| Install optional Bivy bridges/native terminal dependency up front | `BIVY_INSTALL_OPTIONAL_DEPS=1` |\n| Don't touch `~/.bashrc` / `~/.zshrc`; print the PATH line instead | `BIVY_NO_RC_UPDATE=1` |\n\nFor example: `BIVY_CHANNEL=staging curl -fsSL https://bivy.sh/install.sh | bash`.\n\nWorking from a checkout of this repository instead:\n\n```bash\npnpm install\npnpm run setup\n```\n\nSee [`docs/install.md`](docs/install.md) for where data lives, service\nmanagement, and uninstall.\n\n## Updating\n\n```bash\nbivy update\n```\n\n`bivy update` detects how Bivy was installed and does the right thing, then\nwaits for any active session to finish its current turn and restarts the\nbackground service so the node reconnects on the new build:\n\n| Install kind | What `bivy update` does |\n|---|---|\n| npm global (`npm i -g`) | `npm install -g @bivy/bivy@<channel>`, then restart the service |\n| installer / packaged | re-runs `install.sh` (migrating to npm if needed), then restart |\n| git checkout | `git pull --ff-only` + `pnpm install --frozen-lockfile`, then restart |\n| `npx` run | nothing to update — each run already fetches the latest |\n\nUpdates follow the release **channel** recorded at install time — `latest`\n(production) by default, or `staging` if you installed with\n`BIVY_CHANNEL=staging`. Switch channels (the choice is remembered for next\ntime), or skip the wait for a busy session:\n\n```bash\nbivy update --staging # move to the dev channel\nbivy update --stable # move back to production (latest)\nbivy update --force # don't wait for an in-flight turn to finish\n```\n\nThe daemon also checks the registry periodically and posts an in-session notice\nwhen a newer build is available.\n\n## Architecture\n\nBivy has three parts. **Only the first one holds your data.**\n\n```text\n your machine hosted or self-hosted\n\n ┌──────────────┐ ┌─────────┐ ┌───────────────┐\n │ node daemon │ ──dials──▶ │ relay │ ◀────▶ │ control plane │\n │ agents, keys │ outbound │ opaque │ │ accounts, web │\n │ repo, tools │ │ frames │ │ app, metadata │\n └──────────────┘ └─────────┘ └───────────────┘\n ▲ ▲\n └────────── end-to-end encrypted session ───────────┘\n phone · browser · another terminal\n```\n\n- **Node** — a daemon on your machine. Owns the workspace, credentials, and agent\n processes. Serves an API and WebSocket on `http://localhost:4317` plus a\n `/healthz` probe. **It hosts no web UI.**\n- **Relay** — forwards encrypted frames between your node and your devices. Your\n node dials out, so no inbound port is opened. The relay cannot read the frames.\n- **Control plane** — holds your account, node registry, and session index, and\n serves the web/PWA client. Use the hosted one or run your own.\n\nBecause the node serves no UI, a browser or phone needs a control plane — hosted\nat `app.bivy.sh`, or one you deploy yourself. The terminal CLI needs neither.\nInteractive Session traffic is end-to-end encrypted between a Machine and its\npaired devices: the relay never sees plaintext and cannot decrypt it. Who can\n*authorize* a device depends on how you pair — with a QR / `bivy link` pairing,\nor on a self-hosted deployment, the control plane can't read your Sessions\neither; with hosted account sign-in you trust the control plane to authorize\ndevices and to serve the web app that holds the keys. See\n[known limitations](docs/security-model.md#known-limitations-for-0x).\n\nSee [`docs/remote-access.md`](docs/remote-access.md) and\n[`docs/security-model.md`](docs/security-model.md).\n\n## Supported agents\n\n**Bivy maintains wrappers for the popular coding agents below.** Claude Code,\nCodex, Pi, and OpenCode have the richest release-tested capability sets today;\ncapabilities and fidelity still vary by runtime.\n\n| Agent | Command | Notes |\n|---|---|---|\n| Claude Code | `bivy run claude` | Uses the operator-installed `claude` command through an SDK bridge |\n| Codex | `bivy run codex` | Installs `@openai/codex` |\n| Pi | `bivy run pi` | Uses the operator-installed `pi` command and Pi auth/config |\n| OpenCode | `bivy run opencode` | Installs `opencode-ai` |\n| Gemini CLI | `bivy run gemini` | Installs `@google/gemini-cli` |\n| Qwen Code | `bivy run qwen` | Installs `@qwen-code/qwen-code` |\n| Goose | `bivy run goose` | Requires `goose` on PATH |\n| Aider | `bivy run aider` | No session resume (upstream gap) |\n| Cline | `bivy run cline` | Installs `cline` |\n| Crush | `bivy run crush` | No session resume (upstream gap) |\n| Cursor | `bivy run cursor` | ACP-capable |\n| GitHub Copilot | `bivy run copilot` | ACP-capable |\n| Grok | `bivy run grok` | Model selection |\n| Amp | `bivy run amp` | Native thread resume |\n| Auggie | `bivy run auggie` | Headless CLI |\n| Droid | `bivy run droid` | Model selection |\n| Continue | `bivy run continue` | Headless CLI |\n| Kilo Code | `bivy run kilocode` | ACP-capable |\n| Rovo Dev | `bivy run rovodev` | Installed out of band |\n\nAlso defined but hidden from the picker as *Experimental* — runnable via\n`BIVY_RUNTIME=<id>`: Codebuff (`codebuff`, no verified headless mode upstream\nyet), Hermes (`hermes`, generic process adapter), and OpenClaw (`openclaw`,\nCLI adapter only, no resume yet).\n\nAny other command works via `bivy run -- ./your-agent --flags`. ACP-capable\nagents can be promoted to Bivy's governed protocol path for per-tool approvals\nand native resume. To add a reusable process or ACP agent to both the CLI and web\npicker without changing Bivy, run `bivy agent add`, or scaffold and install a\ndeclarative [plugin manifest](docs/plugins.md) with `bivy plugin init`\n(declarative plugins are Experimental, `v1alpha1`, and run out of process).\n\n[`docs/runtime-support-matrix.md`](docs/runtime-support-matrix.md) lists exactly\nwhat each agent supports — resume, model selection, approvals, sandboxing.\n\n## Common commands\n\n```bash\nbivy # show the command overview\nbivy run claude # launch Claude Code as a durable session\nbivy run codex # run a different agent\nbivy sessions # list live and saved sessions\nbivy resume # resume the most recent session\nbivy open # open the web app (requires relay setup)\nbivy automation init # create .bivy/automations.yaml\nbivy agent add # connect an existing ACP or process agent\nbivy plugin list # installed declarative integration packages\nbivy status # config summary and node reachability\nbivy doctor # health check\nbivy logs -f # tail node logs\nbivy update # update Bivy and restart the service\n```\n\nFull command list, flags, and examples: [`docs/cli-reference.md`](docs/cli-reference.md).\n\n## Configuration\n\nThe common knobs:\n\n```bash\nBIVY_WORKSPACE=/path/to/repo # default workspace\nBIVY_SANDBOX=read-only # read-only | workspace-write (default) | danger-full-access\nBIVY_APPROVAL_MODE=risky # never | risky | always | autonomous (default)\n```\n\nCreate and inspect the typed node configuration, or add repository-owned\nsafety/check/retry policy:\n\n```bash\nbivy config init\nbivy config set defaults.agent codex\nbivy config explain defaults.sandbox\nbivy config init --project # .bivy/policy.yaml\n```\n\nSee [`docs/config-as-code.md`](docs/config-as-code.md). Every environment\nvariable and precedence rule lives in\n[`docs/configuration.md`](docs/configuration.md).\n\n## Approvals and sandboxing\n\nThe default approval mode is **`autonomous`**: agents act without per-action\nprompts. How much that actually protects you depends on the runtime. Native-sandbox\nagents enforce the chosen access tier; structured runtimes also pass tool calls\nthrough Bivy's policy and approval layer. Process agents that Bivy cannot\nintercept run with your OS user permissions — the picker flags this and requires\nconfirmation before you pick that path.\n\nWhere Bivy receives structured shell/file calls, a heuristic floor blocks known\ncatastrophic commands and structured writes outside the workspace, and a\nbackstop set (force-push, publish, deploy, sudo) pauses for a human. This catches\naccidents; **it is not an adversarial isolation boundary.**\n\nWant to be asked about more? Set the mode explicitly:\n\n```bash\nBIVY_APPROVAL_MODE=risky # prompt on risky shell commands and file edits\nBIVY_APPROVAL_MODE=always # prompt on all shell commands and file edits\nBIVY_APPROVAL_MODE=never # no prompts; structured-tool heuristic blocks still apply where available\n```\n\nApprove from the terminal, browser, or phone.\n\nSandbox tiers (`read-only`, `workspace-write`, `danger-full-access`) are enforced\nnatively by agents that support them — Codex, Claude Code, Gemini CLI, Qwen Code.\nAgents without a native sandbox may expose structured tool or MCP controls, but\nthose don't cover activity the agent performs outside those channels; some\nprocess adapters run entirely with your user permissions. Check the picker's\nProtection label. **Bivy does not currently ship its own OS-level jail.**\n\n## Credentials\n\nInteractive prompts, transcripts, and workspace files stay encrypted across the\nrelay. Credentials can remain on a Machine or in a vault you control:\n\n```bash\nbivy secrets list\nbivy secrets set github.repo-token\nbivy secrets ref github.repo-token op://Bivy/GitHub/repo-token\nbivy secrets doctor\n```\n\n`secret://`, `env://`, and `op://` (1Password) references resolve on demand when\nthe daemon provisions an agent run, so raw values never sit in your config.\n\n**One deliberate exception to relay blindness:** if you explicitly enable hosted\nunattended provisioning, Bivy Cloud may store encrypted cloud, repository,\nmodel, or key-escrow material that the service can technically access. Treat this\nas an explicit hosted-custody mode. See the\n[security model](docs/security-model.md#what-the-control-plane-sees) and\n[`docs/key-management.md`](docs/key-management.md).\n\n## Automations as code\n\nDefine governed jobs in `.bivy/automations.yaml`, validate them, and simulate\ntrigger events locally before applying anything:\n\n```bash\nbivy automation init\nbivy automation validate\nbivy automation test --event .bivy/events/failed-ci.yaml\nbivy automation apply\n```\n\nInstructions are encrypted on the applying node before upload. Safety policy\nlives beside the job — sandbox, approval mode, and a hard attempt ceiling that\nretry/fallback rules cannot exceed. See\n[`docs/automations-as-code.md`](docs/automations-as-code.md).\n\n## GitHub Runs\n\nLabel an issue `bivy` (or `bivy/<machine>` to target a Machine), or mention the\nBivy GitHub App in a comment. Bivy creates a Run on the selected Machine, uses an\nisolated worktree, executes configured checks, and reports an explicit outcome.\n\nCore applies no commercial usage limits. Bivy Cloud billing and commercial\npolicy live in the separate Cloud repository.\n\nA private GitHub App only installs on the account that owns it, so connect one\napp per GitHub account — one for your personal repos, one per organization\n(`bivy github:app-create --org <org>`). A node can serve several at once, each\nwith its own key and `@`-mention handle.\n\nSee [`docs/github-work-queue.md`](docs/github-work-queue.md).\n\n## Linear Runs\n\nApply `bivy` or `bivy/<machine>` to a Linear issue to create a Run on the selected\nMachine. The Machine fetches issue content directly from Linear, works in an\nisolated GitHub worktree, and asks the agent to open a pull request. See\n[`docs/linear-work-queue.md`](docs/linear-work-queue.md).\n\n## Development\n\n```bash\npnpm install\npnpm run dev # node daemon on http://localhost:4317\npnpm run dev:web # web client dev server (proxies /api and /ws to the node)\n```\n\nChecks — all of these run in CI:\n\n```bash\npnpm run typecheck\npnpm run typecheck:web\npnpm run lint\npnpm run test:unit\npnpm run test:core\npnpm run check:licenses\npnpm run check:secrets\n```\n\nRepository layout:\n\n- `src/` — node daemon, runtime adapters, approvals, secrets, sessions\n- `bin/` — the `bivy` CLI\n- `packages/core` — shared protocol, pairing, wire format\n- `packages/web` — the React/Vite PWA client (`@bivy/web`)\n- `services/relay` — self-hostable relay\n- `services/control-plane` — self-hostable control plane\n- `deploy/` — self-host deployment examples\n\nSee [`CONTRIBUTING.md`](CONTRIBUTING.md).\n\n## Self-hosting\n\nNode, relay, and control plane are all in this repository. Point a node at your\nown deployment by passing URLs to `bivy relay:setup` — re-running it switches an\nexisting node over to the new endpoints:\n\n```bash\nbivy relay:setup \\\n --control-plane https://bivy.example.com \\\n --relay wss://relay.example.com\n```\n\nEach URL has a flag and an environment-variable equivalent (the flag wins):\n\n| Flag | Environment variable | Points at | Default |\n|---|---|---|---|\n| `--control-plane <url>` | `BIVY_CONTROL_PLANE_URL` | accounts, node registry, and the web-app API | hosted (`app.bivy.sh`) |\n| `--relay <wss-url>` | `BIVY_RELAY_URL` | the encrypted-frame relay your node dials out to | hosted |\n| `--client <url>` | `BIVY_CLIENT_BASE_URL` | base URL used when building app/PWA links | the `--control-plane` URL |\n\nSign-in defaults to GitHub device login (`--github`); pass\n`--email you@example.com` for an email magic-link, or `--session-token <token>`\nto skip interactive sign-in. `relay:setup` checks the control plane is reachable,\nenrolls this node, and writes the endpoints to `.bivy/relay.json`, so `bivy open`,\n`bivy link`, and `bivy update` all keep using your deployment afterwards.\n\n**Self-hosting is community-supported** — no SLA, best-effort help via GitHub\nissues. You own TLS, backups, upgrades, and hardening. Start with the\none-command VPS path in\n[`docs/self-host-quickstart.md`](docs/self-host-quickstart.md); the ops\nreference (backups, rotation, security boundary) is\n[`docs/self-host.md`](docs/self-host.md).\n\n## Security\n\nReport vulnerabilities through [GitHub private vulnerability reporting](https://github.com/bivysh/bivy/security/advisories/new).\nPlease don't open a public issue. See [`SECURITY.md`](SECURITY.md) for scope,\nresponse times, and safe harbour, and [`docs/security-model.md`](docs/security-model.md)\nfor the trust model and known limitations.\n\n## License\n\nBivy Core is free and open-source software under the GNU Affero General Public\nLicense, version 3.0 only (AGPL-3.0-only). You may use, study, modify, and\nself-host it under that license. If you modify Bivy and let users interact with\nit over a network, section 13 requires you to offer them the corresponding\nsource code. See [`LICENSE`](LICENSE).\n\n**Where the open-core line is.** Everything in this repository — node, CLI,\nrelay, control plane, and the web/PWA client — is AGPL Core, with no usage\nlimits. **Bivy Cloud** is the hosted operation of that stack plus billing and\nplans, and lives in a separate private repository. Contributions are accepted\nunder the [DCO](CONTRIBUTING.md#certificate-of-origin); there is no CLA.\n",
70
70
  "readmeFilename": "README.md"
71
71
  }