@bivy/bivy 0.20.9-staging.7 → 0.20.9-staging.8

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.
Files changed (110) hide show
  1. package/README.md +1 -2
  2. package/bin/bivy.mjs +13 -56
  3. package/bin/cli-commands.mjs +2 -4
  4. package/bin/guides/talk-to-the-user.md +13 -24
  5. package/dist/agent-instructions.js +4 -10
  6. package/dist/apps/styles.css +2 -17
  7. package/dist/server.js +8 -34
  8. package/dist/session/event-log.js +2 -17
  9. package/dist/session/session-tokens.js +1 -1
  10. package/package.json +2 -2
  11. package/web/assets/{AutomationsView-D445-bV7.js → AutomationsView-DoDebW9z.js} +1 -1
  12. package/web/assets/{ChangesCard-C67_Ay8t.js → ChangesCard-Cm7TsFxw.js} +1 -1
  13. package/web/assets/{LibraryView-DvoQYJWV.js → LibraryView-D_Za_-QQ.js} +1 -1
  14. package/web/assets/{ProviderConnect-DTK0aMt0.js → ProviderConnect-DId9XOvG.js} +1 -1
  15. package/web/assets/{Settings-Vx94BlX0.js → Settings-DjfDtitx.js} +3 -3
  16. package/web/assets/{Sheet-CT1aLpQD.js → Sheet-Q2ePGQye.js} +1 -1
  17. package/web/assets/{Terminal-BY2iHE2c.js → Terminal-DSZ8YBgi.js} +1 -1
  18. package/web/assets/{VoiceSettings-BdNUzVte.js → VoiceSettings-CGxPed9_.js} +1 -1
  19. package/web/assets/{abnfDiagram-VCTEODGH-CojCKgbs.js → abnfDiagram-VCTEODGH-P-N-ZNBN.js} +1 -1
  20. package/web/assets/architecture-7GRP2DOG-Bx9givzi.js +1 -0
  21. package/web/assets/{architectureDiagram-5GKGNRK7-NZZSfjvP.js → architectureDiagram-5GKGNRK7-byCBwDFy.js} +1 -1
  22. package/web/assets/{blockDiagram-I7D4REHJ-eRxQprk8.js → blockDiagram-I7D4REHJ-8Ix3AyxA.js} +1 -1
  23. package/web/assets/{c4Diagram-7LVT6UL2-D6Ixot4g.js → c4Diagram-7LVT6UL2-DsaczzUF.js} +1 -1
  24. package/web/assets/channel-CoFJqi63.js +1 -0
  25. package/web/assets/{chunk-4HAMMTFA-Doy03Ghl.js → chunk-4HAMMTFA-CNU1npt1.js} +1 -1
  26. package/web/assets/{chunk-75Z2AOVW-BJ2SE7Qp.js → chunk-75Z2AOVW-2R8IkZ6K.js} +1 -1
  27. package/web/assets/{chunk-DU6HZSFF-Dt5ewPDF.js → chunk-DU6HZSFF-kfSsBAl-.js} +1 -1
  28. package/web/assets/{chunk-F27PBJKO-D3guFMr5.js → chunk-F27PBJKO-CnHKeJnk.js} +1 -1
  29. package/web/assets/{chunk-GMAD6QVW-BmFGgat8.js → chunk-GMAD6QVW-DN9x2U-t.js} +1 -1
  30. package/web/assets/{chunk-GVQU2GXP-DFBAgenx.js → chunk-GVQU2GXP-kNfCpzk-.js} +1 -1
  31. package/web/assets/{chunk-IMKFNOWR-8gmgNJLT.js → chunk-IMKFNOWR-RQCVneQm.js} +1 -1
  32. package/web/assets/{chunk-L3NEJ4N5-BAK8Ttl3.js → chunk-L3NEJ4N5-DCUakvZ4.js} +1 -1
  33. package/web/assets/{chunk-OSK3NFVY-eYZCFV_w.js → chunk-OSK3NFVY-DwoeBdfs.js} +1 -1
  34. package/web/assets/{chunk-P2QGCYS3-DUPAe9pm.js → chunk-P2QGCYS3-Dr0BtBMh.js} +1 -1
  35. package/web/assets/{chunk-POPQ4Y6H-C0d3tHZO.js → chunk-POPQ4Y6H-9OxfP46b.js} +1 -1
  36. package/web/assets/{chunk-PWAF6VOD-p19FH_YR.js → chunk-PWAF6VOD-CvD7tBZF.js} +1 -1
  37. package/web/assets/{chunk-SHT3W25Y-Rpn9d2Rm.js → chunk-SHT3W25Y-jYLIsjaC.js} +1 -1
  38. package/web/assets/{chunk-SVP7TREG-CAPdAg0C.js → chunk-SVP7TREG-D2yvpT1y.js} +1 -1
  39. package/web/assets/{chunk-TICWLB2K-Bc4LPO1N.js → chunk-TICWLB2K-V1PKTqXh.js} +1 -1
  40. package/web/assets/classDiagram-ZZMXUADV-CoCI6-mr.js +1 -0
  41. package/web/assets/classDiagram-v2-VYDZK3BY-CoCI6-mr.js +1 -0
  42. package/web/assets/{cynefin-OW5HDTMX-C-O66eOU.js → cynefin-OW5HDTMX-S3-HDgGF.js} +1 -1
  43. package/web/assets/{cynefinDiagram-5FMLGOSQ-DbnDizEd.js → cynefinDiagram-5FMLGOSQ-AMnKjokI.js} +1 -1
  44. package/web/assets/{dagre-GXQ25YYZ-uCBN4B8g.js → dagre-GXQ25YYZ-DYFiDtUI.js} +1 -1
  45. package/web/assets/{diagram-S7CK7UJ4-Bh1aP-3D.js → diagram-S7CK7UJ4-B98M2jJY.js} +1 -1
  46. package/web/assets/{diagram-UQ7AKVKN-itw6v7_J.js → diagram-UQ7AKVKN-B3rlK3KL.js} +1 -1
  47. package/web/assets/{diagram-VSXAHHWV-CWEw-WVn.js → diagram-VSXAHHWV-ds4j8snX.js} +1 -1
  48. package/web/assets/{diagram-VX7I27RA-WOJQe21A.js → diagram-VX7I27RA-B2SkgQlQ.js} +1 -1
  49. package/web/assets/{diagram-Z3DM3KII-zhMndslq.js → diagram-Z3DM3KII-6t1yNosN.js} +1 -1
  50. package/web/assets/{ebnfDiagram-PWID7BFC-DwNE2Lvt.js → ebnfDiagram-PWID7BFC-BnpYqLy9.js} +1 -1
  51. package/web/assets/{erDiagram-RLTQ6QDP-zUhuN-9H.js → erDiagram-RLTQ6QDP-BB7HZnkw.js} +1 -1
  52. package/web/assets/eventmodeling-NTZA5JFV-Cz2DLFjE.js +1 -0
  53. package/web/assets/flowDiagram-HODETNUW-Dx9iPIiD.js +1 -0
  54. package/web/assets/{ganttDiagram-EL5Y4UJY-DXdLEN17.js → ganttDiagram-EL5Y4UJY-CRGarlJz.js} +1 -1
  55. package/web/assets/{gitGraph-4MIJSDKK-Klcu9ia9.js → gitGraph-4MIJSDKK-C4LumuQC.js} +1 -1
  56. package/web/assets/{gitGraphDiagram-WWUBYQGX-BU5YHKgV.js → gitGraphDiagram-WWUBYQGX-BEFQqyEF.js} +1 -1
  57. package/web/assets/{index-CplEYkZZ.js → index-DqS1nQdi.js} +2 -2
  58. package/web/assets/{index-eLh2Uq9D.css → index-kWXUrhG8.css} +1 -1
  59. package/web/assets/{info-A6RAGUB7-BH-ebc3S.js → info-A6RAGUB7-D5ABcZgz.js} +1 -1
  60. package/web/assets/{infoDiagram-27XIBGKW-B_KV6pf8.js → infoDiagram-27XIBGKW-DM1N7dMj.js} +1 -1
  61. package/web/assets/{ishikawaDiagram-5VMMS53U-BPAiHRbY.js → ishikawaDiagram-5VMMS53U-DrsbsHXm.js} +1 -1
  62. package/web/assets/{journeyDiagram-3NMN7TZE-DhM5N8fe.js → journeyDiagram-3NMN7TZE-B7VhME2y.js} +1 -1
  63. package/web/assets/{kanban-definition-UXKFOSKX-DOCGhg2w.js → kanban-definition-UXKFOSKX-DH-NTkzb.js} +1 -1
  64. package/web/assets/{line-B4QIneuG.js → line-CpnjiQNF.js} +1 -1
  65. package/web/assets/{mermaid-parser.core-D9vIJCvx.js → mermaid-parser.core-Dltug786.js} +3 -3
  66. package/web/assets/{mermaid.core-DjgZ8mDF.js → mermaid.core--75UrgAP.js} +4 -4
  67. package/web/assets/{mindmap-definition-YA3MSWOX-xnFcHxVA.js → mindmap-definition-YA3MSWOX-D7pwHRXt.js} +1 -1
  68. package/web/assets/{mount-BP8alXTE.js → mount-B0yT9nH2.js} +18 -20
  69. package/web/assets/{packet-AYTQ26CC-yH38dnyf.js → packet-AYTQ26CC-DpU7rkFQ.js} +1 -1
  70. package/web/assets/{pegDiagram-XKGWAZYB-CevgBOwk.js → pegDiagram-XKGWAZYB-CyUHaaZw.js} +1 -1
  71. package/web/assets/{pie-WAS4IAKB-C7xh6GDD.js → pie-WAS4IAKB-k4r1AOoI.js} +1 -1
  72. package/web/assets/{pieDiagram-E7YTZNPT-DKRVnhDT.js → pieDiagram-E7YTZNPT-9vy-IUVb.js} +1 -1
  73. package/web/assets/{quadrantDiagram-AXDQQJYC-DXkOkaBM.js → quadrantDiagram-AXDQQJYC-C-u0AR03.js} +1 -1
  74. package/web/assets/{radar-RG4KPBEZ-CwSQV2mt.js → radar-RG4KPBEZ-DBX_1r-c.js} +1 -1
  75. package/web/assets/{railroad-74A4TZTK-cuHWOkA0.js → railroad-74A4TZTK-C8ed06QJ.js} +1 -1
  76. package/web/assets/railroad-abnf-HS5TGJTU-CH3K-Q4R.js +1 -0
  77. package/web/assets/railroad-ebnf-LZEXJU2U-By7WQSrj.js +1 -0
  78. package/web/assets/railroad-peg-WCYAUIDC-J5fbvVLI.js +1 -0
  79. package/web/assets/{railroadDiagram-O6MQD6OU-CkqsXyWH.js → railroadDiagram-O6MQD6OU-Df9l6PLc.js} +1 -1
  80. package/web/assets/{requirementDiagram-BXWQKSXE-4ysj3ubr.js → requirementDiagram-BXWQKSXE-DMbX29EP.js} +1 -1
  81. package/web/assets/{sankeyDiagram-P5KCCOFB-CTfCHptt.js → sankeyDiagram-P5KCCOFB-DMdKyNDk.js} +1 -1
  82. package/web/assets/{sequenceDiagram-WJ2MYXX4-Bt3hLzHR.js → sequenceDiagram-WJ2MYXX4-BZUKpd2D.js} +1 -1
  83. package/web/assets/{settingsRoute-7vm_XT5W.js → settingsRoute-CxmTmim2.js} +1 -1
  84. package/web/assets/{stateDiagram-D77RDMKH-DbmwF870.js → stateDiagram-D77RDMKH-A5Z3JkUu.js} +1 -1
  85. package/web/assets/stateDiagram-v2-MP3YSRHH-CbBdq37k.js +1 -0
  86. package/web/assets/{swimlanes-42K2YHIH-CS3QAsbj.js → swimlanes-42K2YHIH-DHDsLQTH.js} +1 -1
  87. package/web/assets/swimlanesDiagram-VR7AAH4N-uxsVNpHd.js +8 -0
  88. package/web/assets/{timeline-definition-24CTP7MA-BFY0vMBm.js → timeline-definition-24CTP7MA-MGKQWkpI.js} +1 -1
  89. package/web/assets/{treeView-Q6P3EWNA-BSLxywJi.js → treeView-Q6P3EWNA-C_8iTJWi.js} +1 -1
  90. package/web/assets/{treemap-WGGIJYW6-ZON8ykos.js → treemap-WGGIJYW6-DX8MPwKV.js} +1 -1
  91. package/web/assets/useStore-DnnAeOPe.js +29 -0
  92. package/web/assets/{vennDiagram-4TSXK5OY-BQLzgH0P.js → vennDiagram-4TSXK5OY-WSZd61f7.js} +1 -1
  93. package/web/assets/{wardley-WFR3VGLG-BeuSz2cJ.js → wardley-WFR3VGLG-Bi4EAVkj.js} +1 -1
  94. package/web/assets/{wardleyDiagram-VM6X3IG4-Drcnk9OV.js → wardleyDiagram-VM6X3IG4-L6mpluSU.js} +1 -1
  95. package/web/assets/{xychartDiagram-S5SC5T6Z-DYhLrcFq.js → xychartDiagram-S5SC5T6Z-CwBesblt.js} +1 -1
  96. package/web/index.html +2 -2
  97. package/web/sw.js +1 -1
  98. package/dist/session/suggestions.js +0 -17
  99. package/web/assets/architecture-7GRP2DOG-D-wTFlHA.js +0 -1
  100. package/web/assets/channel-BZ9q5WkS.js +0 -1
  101. package/web/assets/classDiagram-ZZMXUADV-nlLoGCy4.js +0 -1
  102. package/web/assets/classDiagram-v2-VYDZK3BY-nlLoGCy4.js +0 -1
  103. package/web/assets/eventmodeling-NTZA5JFV-D9CvhIf4.js +0 -1
  104. package/web/assets/flowDiagram-HODETNUW-BDdebgr9.js +0 -1
  105. package/web/assets/railroad-abnf-HS5TGJTU-SDwDddPJ.js +0 -1
  106. package/web/assets/railroad-ebnf-LZEXJU2U-CgdgpBax.js +0 -1
  107. package/web/assets/railroad-peg-WCYAUIDC-C98PgZH-.js +0 -1
  108. package/web/assets/stateDiagram-v2-MP3YSRHH-DBdwh_1Q.js +0 -1
  109. package/web/assets/swimlanesDiagram-VR7AAH4N-yJeCEj8W.js +0 -8
  110. package/web/assets/useStore-CNkjpbem.js +0 -29
package/README.md CHANGED
@@ -285,10 +285,9 @@ Every session gets the `bivy` CLI, so any agent with a shell can talk back
285
285
  through the app, not only agents with their own built-in tools:
286
286
 
287
287
  ```bash
288
- bivy notify "Tests are green, PR is up" # chat card + phone push when you're away
288
+ bivy notify # push to your phone so you come back
289
289
  bivy ask "Ship to staging?" --option Yes --option No # waits for your answer
290
290
  bivy attach report.png --caption "Before/after" # show a file in the chat
291
- bivy suggest "Add a dark theme to settings" # a next task you start in one tap
292
291
  bivy context --json # session, workspace, machine, apps
293
292
  ```
294
293
 
package/bin/bivy.mjs CHANGED
@@ -2595,38 +2595,6 @@ async function cmdContext(args = []) {
2595
2595
  console.log(`\nEverything else: ${c.cyan("bivy help")} (or ${c.cyan("bivy help --json")}).`);
2596
2596
  }
2597
2597
 
2598
- // `bivy suggest "<task>" [--title "…"] [--run here|subagents|new] [--session <id>]`
2599
- // — propose a task the user can start in one tap, in this session or beside it. For an agent
2600
- // offering next steps: write the task as a complete instruction.
2601
- async function cmdSuggest(args = []) {
2602
- const usage = 'Usage: bivy suggest "<task>" [--title "short label"] [--run here|subagents|new] [--session <id>] [--json]';
2603
- if (args.includes("-h") || args.includes("--help")) {
2604
- console.log(`${usage}\n\nPost a task the user can start in one tap: in this session, through your sub-agents, or in a parallel session that works in its own copy of the project. Write it as a complete instruction, with paths relative to the project root.\n\n--run says where you recommend running it, which becomes the card's main button: here (builds on this conversation), subagents (independent tasks you can split across your own sub-agents; only if you have them), or new (bigger independent work the user will want to follow in its own session). Without it, one card recommends here and several recommend new.\n\n--json prints {"ok","id"}.`);
2605
- return;
2606
- }
2607
- const json = wantsJson(args);
2608
- const fail = (error) => cliError(error, { json, paint: c.red });
2609
- const flagsWithValue = new Set(["--session", "--title", "--run"]);
2610
- const flag = (name) => {
2611
- const i = args.indexOf(name);
2612
- return i >= 0 && i + 1 < args.length ? args[i + 1] : undefined;
2613
- };
2614
- const text = args.filter((a, i) => !a.startsWith("-") && !(i > 0 && flagsWithValue.has(args[i - 1]))).join(" ").trim();
2615
- const sessionId = resolveAttachSessionId({ sessionFlag: flag("--session"), env: process.env });
2616
- if (!text) return fail({ code: "usage", message: usage, exit: EXIT.usage });
2617
- const run = flag("--run");
2618
- if (run !== undefined && !["here", "subagents", "new"].includes(run)) return fail({ code: "usage", message: `--run is here, subagents or new (got "${run}").`, exit: EXIT.usage });
2619
- if (!sessionId) return fail({ code: "no_session", message: "No session id.", hint: "Run inside an agent session ($BIVY_SESSION_ID) or pass --session <id>.", next: "bivy sessions --json", exit: EXIT.usage });
2620
- const config = loadConfig();
2621
- if (!(await ensureNodeRunning(config))) return fail({ code: "node_unreachable", message: `Could not reach the Bivy node at ${url(config)}.`, next: "bivy status", exit: EXIT.unavailable });
2622
- const res = await sessionPost(config, sessionId, "suggest", { text, title: flag("--title"), run }).catch((error) => error);
2623
- if (res instanceof Error) return fail({ code: "node_unreachable", message: `Could not reach the Bivy node: ${res.message}`, next: "bivy status", exit: EXIT.unavailable });
2624
- const body = await res.json().catch(() => ({}));
2625
- if (!res.ok) return fail(sessionHttpError("Suggest", res.status, body));
2626
- if (json) { console.log(JSON.stringify(body)); return; }
2627
- console.log(c.green("Suggested in the chat. The user can start it in one tap."));
2628
- }
2629
-
2630
2598
  // `bivy title "<title>" [--session <id>]` — rename the session the agent runs in.
2631
2599
  async function cmdTitle(args = []) {
2632
2600
  const usage = 'Usage: bivy title "<title>" [--session <id>] [--json]';
@@ -2674,45 +2642,37 @@ function durationSeconds(text) {
2674
2642
  return m ? Number(m[1]) * (m[2] === "h" ? 3600 : m[2] === "m" ? 60 : 1) : NaN;
2675
2643
  }
2676
2644
 
2677
- // `bivy notify "<message>" [--urgent]` — reach the user: a card in the chat,
2678
- // and a push naming the session (never the text) when nobody has the app open.
2645
+ // `bivy notify [--urgent]` — bring the user back: a push naming the session
2646
+ // (never any text) when nobody has the app open. Nothing is posted in the chat.
2679
2647
  async function cmdNotify(args = []) {
2680
- const usage = 'Usage: bivy notify "<message>" [--urgent] [--session <id>] [--json]';
2648
+ const usage = "Usage: bivy notify [--urgent] [--session <id>] [--json]";
2681
2649
  if (args.includes("-h") || args.includes("--help")) {
2682
2650
  console.log(`${usage}
2683
2651
 
2684
- Send the user a message: it appears as a card in the chat, and their devices get
2685
- a push notification (naming this session, not the text) when nobody has the app
2686
- open. --urgent pushes even while they do. At most one push a minute per session;
2687
- later messages still reach the chat. Use it when you finish long work, get
2688
- blocked, or need the user to look at something. --json prints
2689
- {"ok","id","push":"sent"|"user_watching"|"rate_limited","userWatching"}.`);
2652
+ Push to the user's devices so they come back to this session. The push names the
2653
+ session, nothing else, and nothing is posted in the chat: say what you need in
2654
+ your reply. It is sent only when nobody has the app open; --urgent pushes even
2655
+ while they do. At most one push a minute per session. You don't need it when you
2656
+ finish: Bivy already pushes when a turn ends while the user is away. --json prints
2657
+ {"ok","push":"sent"|"user_watching"|"rate_limited"|"unavailable","userWatching"}.`);
2690
2658
  return;
2691
2659
  }
2692
2660
  const json = wantsJson(args);
2693
2661
  const fail = (error) => cliError(error, { json, paint: c.red });
2694
2662
  const parsed = parseSessionArgs(args, ["--session"]);
2695
2663
  if (parsed.error) return fail({ code: "usage", message: parsed.error, exit: EXIT.usage });
2696
- const text = parsed.positional.join(" ").trim();
2697
- if (!text) return fail({ code: "usage", message: usage, exit: EXIT.usage });
2698
2664
  const sessionId = resolveAttachSessionId({ sessionFlag: parsed.one("--session"), env: process.env });
2699
2665
  if (!sessionId) return fail({ code: "no_session", message: "No session id.", hint: "Run inside an agent session ($BIVY_SESSION_ID) or pass --session <id>.", next: "bivy sessions --json", exit: EXIT.usage });
2700
2666
  const config = loadConfig();
2701
2667
  if (!(await ensureNodeRunning(config))) return fail({ code: "node_unreachable", message: `Could not reach the Bivy node at ${url(config)}.`, next: "bivy status", exit: EXIT.unavailable });
2702
- const res = await sessionPost(config, sessionId, "notify", { text, urgent: parsed.has("--urgent") }).catch((error) => error);
2668
+ const res = await sessionPost(config, sessionId, "notify", { urgent: parsed.has("--urgent") }).catch((error) => error);
2703
2669
  if (res instanceof Error) return fail({ code: "node_unreachable", message: `Could not reach the Bivy node: ${res.message}`, next: "bivy status", exit: EXIT.unavailable });
2704
2670
  const body = await res.json().catch(() => ({}));
2705
2671
  if (!res.ok) return fail(sessionHttpError("Notify", res.status, body));
2706
2672
  if (json) { console.log(JSON.stringify(body)); return; }
2707
- const noPush = { user_watching: "the user has the app open; add --urgent to push anyway", rate_limited: "this session pushed less than a minute ago", unavailable: "this machine isn't signed in to a Bivy account" }[body.push];
2708
- // A terminal run (`bivy run`) has no chat to hold a card: the push is the notice.
2709
- if (body.posted === false) {
2710
- if (body.push === "sent") console.log(c.green("Pushed to the user's devices."));
2711
- else console.log(c.yellow(`Not delivered: a terminal run has no chat to post in, and no push was sent (${noPush ?? body.push}).`));
2712
- return;
2713
- }
2714
- const pushed = body.push === "sent" ? "and pushed to the user's devices" : noPush ? `(no push: ${noPush})` : "";
2715
- console.log(c.green(`Posted in the chat ${pushed}.`.replace(" .", ".")));
2673
+ if (body.push === "sent") { console.log(c.green("Pushed to the user's devices.")); return; }
2674
+ const noPush = { user_watching: "the user has the app open, so they see your reply; add --urgent to push anyway", rate_limited: "this session pushed less than a minute ago", unavailable: "this machine isn't signed in to a Bivy account" }[body.push];
2675
+ console.log(c.yellow(`No push sent: ${noPush ?? body.push}.`));
2716
2676
  }
2717
2677
 
2718
2678
  // `bivy ask "<question>" [--option …]` — the question card, for any agent.
@@ -6515,9 +6475,6 @@ An agent's own --help passes through, e.g. 'bivy run claude --help'.`);
6515
6475
  case "attach":
6516
6476
  await cmdAttach(args);
6517
6477
  break;
6518
- case "suggest":
6519
- await cmdSuggest(args);
6520
- break;
6521
6478
  case "title":
6522
6479
  await cmdTitle(args);
6523
6480
  break;
@@ -44,14 +44,12 @@ export const COMMANDS = [
44
44
  { name: "context", group: "session", scope: "session", json: true, usage: "context [--json]", summary: "Where this agent is running: session, workspace, machine, apps, and what to run next",
45
45
  tools: [{ name: "bivy_context", description: "Where you are running inside Bivy: your session, agent, workspace and git branch, the machine, apps you have published, whether the user has the app open, and the commands that reach them.", input: {}, argv: ["context"] }] },
46
46
  { name: "attach", group: "session", scope: "session", json: true, usage: 'attach <file> [--caption "…"] [--artifact]', summary: "Show a local file or image to the user in the chat" },
47
- { name: "notify", group: "session", scope: "session", json: true, usage: 'notify "<message>" [--urgent]', summary: "Message the user: a card in the chat, and a push when they're away",
48
- tools: [{ name: "notify_user", description: "Send the user a message: a card in the chat, and a push to their phone when nobody has the app open (the push names the session, not the text). Use it when long work finishes, you are blocked, or something needs their attention. At most one push a minute per session.", input: { message: { type: "string", description: "What to tell the user.", required: true }, urgent: { type: "boolean", description: "Push even while the user has the app open." } }, argv: ["notify", { arg: "message" }, { when: "urgent", flag: "--urgent" }] }] },
47
+ { name: "notify", group: "session", scope: "session", json: true, usage: "notify [--urgent]", summary: "Push to the user's phone so they come back (nothing posted in the chat)",
48
+ tools: [{ name: "notify_user", description: "Push to the user's phone so they come back to this session, e.g. when you are blocked on them while still working. The push names the session only and nothing is posted in the chat, so say what you need in your reply. Not needed when you finish: Bivy already pushes when a turn ends while they are away. Sent only when nobody has the app open, at most one a minute per session.", input: { urgent: { type: "boolean", description: "Push even while the user has the app open." } }, argv: ["notify", { when: "urgent", flag: "--urgent" }] }] },
49
49
  { name: "ask", group: "session", scope: "session", json: true, usage: 'ask "<question>" [--option A --option B] [--async]', summary: "Ask the user a question and wait for the answer", subcommands: ["status", "wait"],
50
50
  tools: [{ name: "ask_user", description: "Ask the user a question in the chat and wait for their answer (their phone gets a push). Offer 2-8 options, or none for a free-text answer; they can always write their own. Returns {status, answer}: status is answered, dismissed (decide yourself) or expired.", input: { question: { type: "string", description: "The question.", required: true }, options: { type: "array", items: { type: "string" }, description: "Choices to pick from (2-8)." }, multiple: { type: "boolean", description: "Allow several choices." }, header: { type: "string", description: "A short label for the card." }, timeout_seconds: { type: "number", description: "How long to wait (default 600)." } }, argv: ["ask", { arg: "question" }, { flag: "--option", from: "options" }, { when: "multiple", flag: "--multi" }, { flag: "--header", from: "header" }, { flag: "--timeout", from: "timeout_seconds" }] }] },
51
51
  { name: "title", group: "session", scope: "session", json: true, usage: 'title "<title>"', summary: "Rename this session",
52
52
  tools: [{ name: "set_session_title", description: "Rename this session in the user's session list. Use a short title (under 60 characters) that says what the work is, when the first message made a poor title or the work changed direction.", input: { title: { type: "string", description: "The new title.", required: true } }, argv: ["title", { arg: "title" }] }] },
53
- { name: "suggest", group: "session", scope: "session", json: true, usage: 'suggest "<task>" [--title "label"] [--run here|subagents|new]', summary: "Propose a task the user can start in one tap",
54
- tools: [{ name: "suggest_task", description: "Propose a next step the user can start in one tap: in this session, through your sub-agents, or in a parallel session with its own copy of the project. Write the task as a complete instruction with paths relative to the project root. Post one per idea instead of listing them.", input: { task: { type: "string", description: "The complete instruction.", required: true }, title: { type: "string", description: "A short label for the card." }, run: { type: "string", enum: ["here", "subagents", "new"], description: "Where you recommend running it: here (builds on this conversation), subagents (independent tasks you can split across your own sub-agents; only if you have them), or new (bigger independent work in its own session). Defaults: here for one card, new for several." } }, argv: ["suggest", { arg: "task" }, { flag: "--title", from: "title" }, { flag: "--run", from: "run" }] }] },
55
53
  { name: "app", group: "session", scope: "session", json: true, usage: "app <publish|shot|present|share|run|…>", summary: "Live previews: publish a web/terminal/desktop app, screenshot it, present it", subcommands: ["publish", "list", "remove", "shot", "present", "share", "notes", "run", "click", "type", "key", "scroll", "drag", "move", "menu"],
56
54
  tools: [
57
55
  { name: "app_publish", description: "Give the user a live preview of something with a UI, from an app manifest file (web server port, static build, terminal, or desktop app). Run `bivy app --help` for the manifest format.", input: { manifest_path: { type: "string", description: "Path to the manifest JSON.", required: true } }, argv: ["app", "publish", { arg: "manifest_path" }] },
@@ -1,18 +1,24 @@
1
1
  # Talk to the user
2
2
 
3
- Summary: When to answer in chat, when to notify, when to ask and wait, and how to offer next steps.
3
+ Summary: When to answer in chat, when to notify, and when to ask and wait.
4
4
 
5
5
  Your chat reply is the default. Reach for these when the chat alone won't do.
6
6
 
7
7
  ## They may be away: notify
8
8
 
9
- bivy notify "Migration finished: 3 tables rewritten, all tests pass."
10
- bivy notify --urgent "Production is serving the old build."
9
+ bivy notify
10
+ bivy notify --urgent
11
11
 
12
- Posts a card in the chat. When nobody has the app open, their devices get a push
13
- naming this session (the text stays in the chat). `--urgent` pushes even while
14
- they are looking. One push a minute per session; don't send one per step.
15
- Good moments: long work finished, you are blocked, something needs a look.
12
+ Pushes to their devices so they come back to this session. The push names the
13
+ session only and nothing is posted in the chat, so say what you need in your
14
+ reply. It goes out only when nobody has the app open; `--urgent` pushes even
15
+ while they are looking. One push a minute per session.
16
+
17
+ You don't need it when you finish: Bivy already pushes when a turn ends while
18
+ they are away. Use it while you keep working and need them, for example when a
19
+ long run is blocked on something only they can do.
20
+
21
+ Next steps and options go in your reply as plain text.
16
22
 
17
23
  ## You need a decision: ask
18
24
 
@@ -24,23 +30,6 @@ dismissed it (use your judgment), exit 5 that `--timeout` (default 10m) passed.
24
30
  For long waits use `--async`, keep working, and later `bivy ask wait <id>`.
25
31
  Ask only when you can't reasonably decide yourself.
26
32
 
27
- ## Offer next steps: suggest
28
-
29
- bivy suggest "Add a GET /version endpoint that returns the package version." --title "Add /version"
30
-
31
- Each suggestion is a card the user can start in one tap: here, through your
32
- sub-agents, or in a parallel session with its own copy of the project. Write it as
33
- a complete instruction with paths relative to the project root. Post one per idea
34
- instead of a bulleted list.
35
-
36
- `--run` picks the card's main button; the others stay one tap away:
37
-
38
- | `--run` | When |
39
- |---|---|
40
- | `here` | It builds on this conversation, or it's small. Default for a single card. |
41
- | `subagents` | Independent tasks you can split across your own sub-agents and report back on. Only if you have sub-agents. |
42
- | `new` | Bigger independent work the user will want to follow, review or merge on its own. Default for several cards. |
43
-
44
33
  ## Name the session: title
45
34
 
46
35
  bivy title "Fix login redirect loop"
@@ -39,18 +39,12 @@ export const BIVY_AGENT_NOTE = [
39
39
  "can check your work; `bivy app present` tells the user a visible change is ready to look at, and " +
40
40
  "`.bivy/scenarios/*.json` files let them open it in the states that matter (errors, empty, slow) with `--try`. " +
41
41
  "`bivy app --help` has the details.",
42
- "- Proposing tasks the user could hand you (next steps, ideas, options): post each with " +
43
- '`bivy suggest "<complete instruction>" [--title "short label"] [--run here|subagents|new]` instead of only ' +
44
- "listing them. The user can start each in one tap, in this session, through your sub-agents, or in a parallel " +
45
- "one that works in its own copy of the project, so write it to stand on its own, with paths relative to the " +
46
- "project root. `--run` is the one you recommend: here when it builds on this conversation, subagents for " +
47
- "independent tasks you can split and supervise (only if you have sub-agents), new for bigger work worth its " +
48
- "own session.",
49
42
  '- If this session\'s title (taken from the first message) doesn\'t say what the work is, or the work changes ' +
50
43
  'direction: `bivy title "<short title>"`.',
51
- "- When you finish long work, get blocked, or need the user to look at something: " +
52
- '`bivy notify "<message>"` (a chat card, plus a push to their phone when they are away). To ask and wait for ' +
53
- 'an answer: `bivy ask "<question>" [--option A --option B]` prints their answer (or use your own ask-the-user tool).',
44
+ "- Your chat reply is what the user reads; when you finish while they are away, Bivy pushes to their phone for you. " +
45
+ 'To bring them back while you keep working: `bivy notify` (a push only; say what you need in the chat). To ask ' +
46
+ 'and wait for an answer: `bivy ask "<question>" [--option A --option B]` prints their answer (or use your own ' +
47
+ "ask-the-user tool).",
54
48
  "- Only when the user asks for another agent or another of their machines to take part: " +
55
49
  '`bivy delegate "<self-contained task>" --agent <id> [--machine <name>] --wait` runs it there and prints ' +
56
50
  "its answer and any branch/PR; `--to codex,grok@<machine>` asks several to compare, `bivy delegate machines` " +
@@ -3241,24 +3241,9 @@ p.setup-note .btn.link { min-height: 44px; margin-block: calc((1lh - 44px) / 2);
3241
3241
  .app-row-meta { color: var(--muted); font-size: var(--text-xs); line-height: var(--lh-xs); }
3242
3242
  .app-row-action { flex: none; display: flex; align-items: center; gap: var(--space-1); }
3243
3243
  .app-message { margin-block: var(--space-2); }
3244
- /* Suggested task (SuggestionCard.tsx): a task the agent proposed, one tap to
3245
- start in its own session. Shell from .card; layout only here. */
3246
- .suggestion-card { max-inline-size: 440px; display: flex; flex-direction: column; gap: var(--space-1); }
3247
3244
  /* The small label above a chat card's content, shared by every agent-posted card. */
3248
- .suggestion-eyebrow, .delegation-eyebrow, .notice-eyebrow { margin: 0; color: var(--muted); font-size: var(--text-xs); font-weight: var(--weight-semibold); }
3249
- .suggestion-title { margin: 0; font-size: var(--text-base); font-weight: var(--weight-semibold); overflow-wrap: anywhere; }
3250
- .suggestion-text { margin: 0; color: var(--muted); font-size: var(--text-sm); white-space: pre-wrap; overflow-wrap: anywhere; display: -webkit-box; -webkit-line-clamp: 4; -webkit-box-orient: vertical; overflow: hidden; }
3251
- .suggestion-text[data-expanded="true"] { display: block; overflow: visible; }
3252
- .suggestion-actions { display: flex; flex-wrap: wrap; gap: var(--space-2); margin-top: var(--space-2); }
3253
- .suggestion-bar, .suggestion-all { display: flex; flex-direction: column; gap: var(--space-1); margin-top: var(--space-2); }
3254
- .suggestion-all { padding-top: var(--space-2); border-top: 1px solid var(--line); }
3255
- .suggestion-bar .suggestion-actions, .suggestion-all .suggestion-actions { margin-top: 0; }
3256
- .suggestion-hint { margin: 0; color: var(--muted); font-size: var(--text-xs); }
3257
- /* In a set, the title is the checkbox's label, so the whole line is the tap target. */
3258
- .suggestion-pick { display: flex; align-items: flex-start; gap: var(--space-2); cursor: pointer; }
3259
- .suggestion-pick input { flex: none; inline-size: 18px; block-size: 18px; margin: 2px 0 0; accent-color: var(--accent); }
3260
- .suggestion-status { margin: var(--space-2) 0 0; display: flex; align-items: center; gap: var(--space-2); color: var(--ok); font-size: var(--text-sm); font-weight: var(--weight-medium); }
3261
- /* Notice (NoticeCard.tsx): a message the agent sent with `bivy notify`.
3245
+ .delegation-eyebrow, .notice-eyebrow { margin: 0; color: var(--muted); font-size: var(--text-xs); font-weight: var(--weight-semibold); }
3246
+ /* Notice (NoticeCard.tsx): a message from Bivy, e.g. applied automations.
3262
3247
  Shell and tone from .card, the urgent marker from .badge. */
3263
3248
  .notice-card { max-inline-size: 440px; display: flex; flex-direction: column; gap: var(--space-1); }
3264
3249
  .notice-eyebrow { display: flex; align-items: center; gap: var(--space-2); }
package/dist/server.js CHANGED
@@ -80,7 +80,6 @@ import { exportProviderAuth, exportAccountApiKeys, exportAccountOAuthCredentials
80
80
  import { listProviders } from "./runtime/provider-catalog.js";
81
81
  import { exportLocalModels, importLocalModels } from "./runtime/local-model-store.js";
82
82
  import { sessionLikeFields } from "./session/start-like.js";
83
- import { MAX_SUGGESTION_TEXT, MAX_SUGGESTION_TITLE, SUGGESTION_RUNS, isTaskSuggestion } from "./session/suggestions.js";
84
83
  import { BIVY_AGENT_NOTE, mergeSyncedAgentInstructions, readAgentInstructions, sessionInstructions, writeAgentInstructions, MAX_AGENT_INSTRUCTIONS_BYTES } from "./agent-instructions.js";
85
84
  import { execEphemeralRequest } from "./ephemeral-exec.js";
86
85
  import { ApprovalManager } from "./approval.js";
@@ -143,7 +142,7 @@ import { AgentQuestions, askQuestionsFrom } from "./session/agent-questions.js";
143
142
  import { AutomationProposals, describeChange } from "./session/automation-proposals.js";
144
143
  import { createSessionTokenCodec, isSessionToken, sessionTokenAllows } from "./session/session-tokens.js";
145
144
  import { setSessionTokenSigner } from "./runtime/session-env.js";
146
- import { MAX_NOTICE_TEXT, isAgentNotice, noticePush } from "./session/notices.js";
145
+ import { MAX_NOTICE_TEXT, noticePush } from "./session/notices.js";
147
146
  import { createForkCommands } from "./controllers/fork-commands.js";
148
147
  import { createGithubCommands } from "./controllers/github-commands.js";
149
148
  import { createCredentialCommands } from "./controllers/credential-commands.js";
@@ -11742,10 +11741,9 @@ app.get("/api/session/:id/context", (req, res) => {
11742
11741
  previewAvailable,
11743
11742
  }));
11744
11743
  });
11745
- // `bivy notify "<message>"`: a card in the chat, and a push naming the session
11746
- // (never the text) when nobody has the app open, or when it's urgent. At most
11747
- // one push a minute per session; later notices still post a card. A `bivy run`
11748
- // terminal has no chat to hold the card, so its notice is the push alone.
11744
+ // `bivy notify`: a push naming the session (never any text) when nobody has the
11745
+ // app open, or when it's urgent. At most one push a minute per session. It adds
11746
+ // nothing to the chat: what the agent has to say belongs in its reply there.
11749
11747
  const lastNotifyPush = new Map();
11750
11748
  app.post("/api/session/:id/notify", (req, res) => {
11751
11749
  const record = openSessions.get(String(req.params.id));
@@ -11753,16 +11751,9 @@ app.post("/api/session/:id/notify", (req, res) => {
11753
11751
  const sessionId = record?.id ?? run?.sessionId;
11754
11752
  if (!sessionId)
11755
11753
  return res.status(404).json({ error: "Session not found" });
11756
- const text = typeof req.body?.text === "string" ? req.body.text.trim() : "";
11757
- const notice = { id: `notice-${randomBytes(8).toString("hex")}`, text, ...(req.body?.urgent === true ? { urgent: true } : {}) };
11758
- if (!isAgentNotice(notice))
11759
- return res.status(400).json({ error: `A notice needs text (up to ${MAX_NOTICE_TEXT} characters).` });
11760
- if (record) {
11761
- eventLog.appendNotice(record.id, { afterMessageCount: record.session.getMessages().length, notice });
11762
- broadcast(stampSessionEvent({ type: "session.event", sessionId: record.id, event: { type: "notice", id: notice.id, notice } }));
11763
- }
11754
+ const urgent = req.body?.urgent === true;
11764
11755
  const watching = Boolean(record) && (clients.size > 0 || (relay?.clientCount ?? 0) > 0);
11765
- const push = noticePush({ urgent: notice.urgent, userWatching: watching, lastPushAt: lastNotifyPush.get(sessionId), now: Date.now(), pushConfigured: Boolean(sessionAdvertiseTarget) });
11756
+ const push = noticePush({ urgent, userWatching: watching, lastPushAt: lastNotifyPush.get(sessionId), now: Date.now(), pushConfigured: Boolean(sessionAdvertiseTarget) });
11766
11757
  if (push === "sent") {
11767
11758
  lastNotifyPush.set(sessionId, Date.now());
11768
11759
  void sendNotificationHint({
@@ -11770,10 +11761,10 @@ app.post("/api/session/:id/notify", (req, res) => {
11770
11761
  sessionId,
11771
11762
  targetSessionId: sessionId,
11772
11763
  title: sessionNotifyLabel(record, run?.name || "A terminal session"),
11773
- body: "Has a message for you — tap to read it.",
11764
+ body: "Wants you to take a look — tap to open the session.",
11774
11765
  });
11775
11766
  }
11776
- res.json({ ok: true, id: notice.id, posted: Boolean(record), push, userWatching: watching });
11767
+ res.json({ ok: true, push, userWatching: watching });
11777
11768
  });
11778
11769
  // `bivy ask`: the question card and "needs your input" push, for any agent.
11779
11770
  // Returns at once with an id; `/wait` blocks up to 240s per call (under common
@@ -11831,23 +11822,6 @@ app.post("/api/session/:id/title", (req, res) => {
11831
11822
  sessionNamer.setSessionName(record, title);
11832
11823
  res.json({ ok: true, title });
11833
11824
  });
11834
- // `bivy suggest "<task>"`: the agent proposes a task the user can start in one
11835
- // tap, here, through this agent's sub-agents, or in a parallel session; `run`
11836
- // is the one the agent recommends (see packages/web SuggestionCard).
11837
- app.post("/api/session/:id/suggest", (req, res) => {
11838
- const record = openSessions.get(String(req.params.id));
11839
- if (!record)
11840
- return res.status(404).json({ error: "Session not found" });
11841
- const text = typeof req.body?.text === "string" ? req.body.text.trim() : "";
11842
- const title = typeof req.body?.title === "string" && req.body.title.trim() ? req.body.title.trim() : undefined;
11843
- const run = req.body?.run || undefined;
11844
- const suggestion = { id: `suggestion-${randomBytes(8).toString("hex")}`, text, ...(title ? { title } : {}), ...(run ? { run } : {}) };
11845
- if (!isTaskSuggestion(suggestion))
11846
- return res.status(400).json({ error: `A suggestion needs text (up to ${MAX_SUGGESTION_TEXT} characters), an optional title (up to ${MAX_SUGGESTION_TITLE}) and an optional run (${SUGGESTION_RUNS.join(", ")}).` });
11847
- eventLog.appendSuggestion(record.id, { afterMessageCount: record.session.getMessages().length, suggestion });
11848
- broadcast(stampSessionEvent({ type: "session.event", sessionId: record.id, event: { type: "suggestion", id: suggestion.id, suggestion } }));
11849
- res.json({ ok: true, id: suggestion.id });
11850
- });
11851
11825
  // Explicit child Run API for first-party/user-directed workflows. These routes
11852
11826
  // are intentionally not advertised to agents as tools; the service still
11853
11827
  // authorizes every status lookup against this parent Session's provenance.
@@ -31,7 +31,6 @@ import path from "node:path";
31
31
  import { normalizedIntermediateText, thinkingTextFromContent, mergeTranscript } from "./transcript-merge.js";
32
32
  import { DELEGATION_BLOCK, isDelegationCard } from "./delegations.js";
33
33
  import { APP_PIN_BLOCK, APP_PUBLICATION_BLOCK, APP_REVIEW_BLOCK, isAppPin, isAppReference, isAppReview } from "../apps/types.js";
34
- import { SUGGESTION_BLOCK, isTaskSuggestion } from "./suggestions.js";
35
34
  import { NOTICE_BLOCK, isAgentNotice } from "./notices.js";
36
35
  /**
37
36
  * Decompose a transcript message into serialization-independent "atoms": the
@@ -333,12 +332,6 @@ function isAppPublication(value) {
333
332
  const entry = value;
334
333
  return entry.bivyKind === "app-publication" && typeof entry.id === "string" && typeof entry.createdAt === "number" && typeof entry.afterMessageCount === "number" && isAppReference(entry.app);
335
334
  }
336
- function isSuggestionEntry(value) {
337
- if (!value || typeof value !== "object")
338
- return false;
339
- const entry = value;
340
- return entry.bivyKind === "suggestion" && typeof entry.id === "string" && typeof entry.createdAt === "number" && typeof entry.afterMessageCount === "number" && isTaskSuggestion(entry.suggestion);
341
- }
342
335
  function isNoticeEntry(value) {
343
336
  if (!value || typeof value !== "object")
344
337
  return false;
@@ -364,7 +357,7 @@ function isAppPinEntry(value) {
364
357
  return entry.bivyKind === "app-pin" && typeof entry.id === "string" && typeof entry.createdAt === "number" && typeof entry.afterMessageCount === "number" && isAppPin(entry.pin);
365
358
  }
366
359
  function isRecord(value) {
367
- return isDelegationEntry(value) || isOverlay(value) || isBase(value) || isForkDisplay(value) || isAttachment(value) || isOutboundAttachment(value) || isInlineImage(value) || isAppPublication(value) || isAppReviewEntry(value) || isAppPinEntry(value) || isSuggestionEntry(value) || isNoticeEntry(value);
360
+ return isDelegationEntry(value) || isOverlay(value) || isBase(value) || isForkDisplay(value) || isAttachment(value) || isOutboundAttachment(value) || isInlineImage(value) || isAppPublication(value) || isAppReviewEntry(value) || isAppPinEntry(value) || isNoticeEntry(value);
368
361
  }
369
362
  /**
370
363
  * Fold attachment records into a text→refs list: last write wins per text (a
@@ -507,15 +500,11 @@ export function replayExtras(entries) {
507
500
  role: "assistant", content: [{ type: DELEGATION_BLOCK, delegation: entry.delegation }],
508
501
  id: `delegation-${entry.id}`, afterMessageCount: entry.afterMessageCount, createdAt: entry.createdAt,
509
502
  }));
510
- const suggestions = entries.filter((entry) => entry.bivyKind === "suggestion").map((entry) => ({
511
- role: "assistant", content: [{ type: SUGGESTION_BLOCK, suggestion: entry.suggestion }],
512
- id: entry.id, afterMessageCount: entry.afterMessageCount, createdAt: entry.createdAt,
513
- }));
514
503
  const notices = entries.filter((entry) => entry.bivyKind === "notice").map((entry) => ({
515
504
  role: "assistant", content: [{ type: NOTICE_BLOCK, notice: entry.notice }],
516
505
  id: entry.id, afterMessageCount: entry.afterMessageCount, createdAt: entry.createdAt,
517
506
  }));
518
- return [...foldIntermediate(intermediate), ...foldTool(tool), ...replayOutboundAttachments(entries), ...publications, ...cards, ...pinCards, ...delegationCards, ...suggestions, ...notices];
507
+ return [...foldIntermediate(intermediate), ...foldTool(tool), ...replayOutboundAttachments(entries), ...publications, ...cards, ...pinCards, ...delegationCards, ...notices];
519
508
  }
520
509
  /**
521
510
  * Fold the outbound (agent-sent) attachment records into time-anchored synthetic
@@ -832,10 +821,6 @@ export class EventLog {
832
821
  this.load(id);
833
822
  this.enqueue(id, `pin:${entry.pin.id}`, { bivyKind: "app-pin", createdAt: entry.createdAt ?? Date.now(), afterMessageCount: entry.afterMessageCount, id: entry.pin.id, pin: structuredClone(entry.pin) });
834
823
  }
835
- appendSuggestion(id, entry) {
836
- this.load(id);
837
- this.enqueue(id, `suggestion:${entry.suggestion.id}`, { bivyKind: "suggestion", createdAt: Date.now(), afterMessageCount: entry.afterMessageCount, id: entry.suggestion.id, suggestion: { ...entry.suggestion } });
838
- }
839
824
  appendNotice(id, entry) {
840
825
  this.load(id);
841
826
  this.enqueue(id, `notice:${entry.notice.id}`, { bivyKind: "notice", createdAt: Date.now(), afterMessageCount: entry.afterMessageCount, id: entry.notice.id, notice: { ...entry.notice } });
@@ -44,7 +44,7 @@ export function createSessionTokenCodec(key = randomBytes(32)) {
44
44
  */
45
45
  export const SESSION_TOKEN_ROUTES = [
46
46
  { method: "GET", path: /^\/api\/session\/([^/]+)\/context$/, session: "path" },
47
- { method: "POST", path: /^\/api\/session\/([^/]+)\/(attach|suggest|notify|title)$/, session: "path" },
47
+ { method: "POST", path: /^\/api\/session\/([^/]+)\/(attach|notify|title)$/, session: "path" },
48
48
  { method: "POST", path: /^\/api\/session\/([^/]+)\/ask$/, session: "path" },
49
49
  { method: "GET", path: /^\/api\/session\/([^/]+)\/ask\/[^/]+$/, session: "path" },
50
50
  { method: "POST", path: /^\/api\/session\/([^/]+)\/ask\/[^/]+\/wait$/, session: "path" },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bivy/bivy",
3
- "version": "0.20.9-staging.7",
3
+ "version": "0.20.9-staging.8",
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.",
@@ -65,7 +65,7 @@
65
65
  "lodash-es": "4.18.1",
66
66
  "undici": "8.11.2"
67
67
  },
68
- "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**Local coding agents that show their work. Anywhere.**\n\nBivy is the open-source workspace for coding agents. It runs Claude Code, Codex,\nPi, OpenCode and other agents on your own machine. Reach them from a browser or\nyour phone, with the live session, its terminal, its approvals, and the app\nthey're building right in the chat. Try it, mark what's wrong, and send it back.\n\n<p align=\"center\">\n <img src=\"docs/images/preview-feedback-loop.gif\" width=\"360\"\n alt=\"On a phone: open the app an agent built, circle the packed items, send a note, and compare the result before and after the agent's fix.\">\n</p>\n\nBivy is not another coding agent and not a cloud development machine. The agent\nstays local and does the coding with your model provider. Your repos, tools,\ndatabases, and services stay where they are. Bivy gives you remote access to all\nof it, plus live previews, automations, and review.\n\n- **Live previews.** The running app beside the chat. Mark it, send it back,\n share it with someone who has no Bivy account.\n- **Automations.** GitHub issues, failed CI, Linear, Slack, schedules, and\n webhooks start the work. It comes back as a preview link or a pull request.\n- **Any agent.** Hit a usage limit? Fork the session to another agent, or let\n Bivy retry when the limit resets.\n- **Your machine, anywhere.** Start at your desk, pick the session up on your\n phone. The machine dials out, so there are no ports to open, and session\n traffic is end-to-end encrypted.\n\n**[Start free on Bivy Cloud](https://app.bivy.sh)** ·\n**[Quickstart](docs/quickstart.md)** ·\n**[Documentation](docs/README.md)** ·\n**[Self-host](docs/deploy-images.md)** ·\n**[Website](https://bivy.sh)**\n\n**Recommended:** sign in at [app.bivy.sh](https://app.bivy.sh), then copy your\npersonalized **Connect a Machine** command into a terminal on your Mac or Linux\ncomputer. It installs and enrolls the machine without another Bivy login. The\nbrowser connects automatically; choose a repository and send your first task\nright there.\n\nPrefer starting from the terminal?\n\n```bash\ncurl -fsSL https://bivy.sh/install.sh | bash # install + guided setup\ncd your-repo\nbivy run claude # or codex, pi, opencode\nbivy open # continue in the web app (needs remote setup)\n```\n\nBivy Cloud hosts the app, control plane, and relay—not the machines running your\nagents. Connect a Mac, Linux computer, or existing server and bring your own\nagent subscription, model API key, or local model. You can also self-host the\nentire remote-access stack.\n\n> **Bivy is 0.x software.** Claude Code, Codex, Pi, OpenCode, and Grok are the\n> release-tested paths. Credential sync, resume, handoffs, approvals, and\n> sandboxing depend on the runtime. See the\n> [runtime support matrix](docs/runtime-support-matrix.md).\n\n## Don't just read the diff. Try the app.\n\nBivy lets you **use what the agent built and show it what to fix**, without a\nseparate deployment step.\n\n1. **Ask for a change.** The agent works in your repo with your existing tools\n and local services.\n2. **Try the app.** Bivy finds the dev server the agent starts and opens it\n beside the chat: web apps, terminals, and desktop apps. Use the running app,\n not a screenshot of what the agent says it finished.\n3. **Mark what needs work.** Press and hold anything in the preview to mark it,\n or drag to circle it, then say what you want. **Mark another** keeps\n separate notes separate. The marks go to the agent with the element and page\n context. No screenshots to paste.\n4. **Follow each note.** Every mark stays in the chat as a pin that resolves\n itself: **Changed** when a later run changes what you marked, **Element\n gone** when it leaves the page, **Done** when you say so.\n5. **Review the next version.** A new version waits on the preview pill instead\n of reloading under you. Take it when you're ready, compare before and after,\n inspect the diff and checks, and decide when the work is done.\n6. **Get a second opinion.** Share a preview link for 1 hour, 1 day, or 7 days.\n Teammates and clients try the running app and leave notes without a Bivy\n account. Make it view-only, or **Stop sharing** to end every link at once.\n\nOn Bivy Cloud, preview delivery is built in: no per-app domains, certificates,\npublic ports, or tunnels to configure. Self-hosters configure preview delivery\nonce for their Bivy deployment, not for every app. The app still needs its normal\nbuild or dev-server setup, and **the machine serving it must stay awake and\nonline**. These are development previews, not production hosting.\n\n**Share deliberately:** anyone with a preview link can use that app, including\nits live backend, until the link expires or you stop sharing.\nReviewer notes aren't sent to the agent automatically; you send them or explicitly\nallow agent access. Preview traffic uses HTTPS through the preview relay, not\nsession end-to-end encryption; the relay operator can see it.\n\n[App previews, visual feedback, and sharing →](docs/apps.md)\n\n## One workflow, from trigger to review\n\n```text\nPrompt · GitHub issue · CI failure · Linear · Slack · Schedule · Webhook\n │\n ▼\n Choose machine + agent + model\n + supported credentials\n │\n ▼\n Live agent session\n Join · steer · approve · stop\n │\n ▼\n Try app · inspect changes · checks\n │\n ▼\n Mark up · send feedback · iterate\n │\n ▼\n Share preview · review PR\n```\n\nA **Machine** is a computer or server you connect. A **Session** is live agent\nwork on that machine. A **Run** is delegated background work that creates a\nsession and tracks its outcome. An **Automation** is a reusable definition that\ncreates runs when an event matches.\n\nManual and automated work use the same kind of live session. You can join a run\nwhen it needs help rather than wait for a black-box job to finish.\n\n### Let events start the work\n\nAutomations turn recurring or incoming work into sessions you can join,\nsupervise, and review. An agent picks the task up on your machine and posts\nback a preview link or a pull request, so you try the result where the task\nlives and reply in the same session. Choose the repository, machine, agent,\nmodel, approval mode, sandbox setting, and maximum attempts.\n\n| Trigger | Example workflow |\n|---|---|\n| **GitHub issues and mentions** | Label an issue `bivy` or `bivy/<machine>`, or mention your Bivy GitHub App, to work toward a pull request. |\n| **Failed CI** | Match a failed workflow, ask the agent to reproduce it, make a fix, and run the affected checks. |\n| **Linear** | Label an issue to start work without copying its description into an agent. |\n| **Slack** | Send a request from the conversation where the work came up. |\n| **Schedules** | Run a weekly dependency review, recurring maintenance, or a one-time task. |\n| **Signed webhooks** | Connect alerts, internal tools, or your own event sources. |\n\nConfigure automations in the app or version them with your repository in\n`.bivy/automations.yaml`:\n\n```bash\nbivy automation init\n# Edit the generated definition for your repository and workflow.\nbivy automation validate\nbivy automation test --event .bivy/events/failed-ci.yaml # supply a local event fixture\nbivy automation apply\n```\n\nOr delegate a one-off job without creating an automation:\n\n```bash\nbivy runs start \"Review outdated dependencies and propose a small, tested update.\"\nbivy runs wait <id>\n```\n\nRuns keep routing and lifecycle evidence, check results, and output references\nin a reviewable Receipt. For unattended issue work, Bivy runs declared repository\nchecks after the agent's turn; failed required checks fail the run even if the\nagent reports success. A completed process alone is not proof that the task\nsucceeded.\n\n[Automation recipes →](docs/capability-recipes.md#let-events-start-runs) ·\n[Automations as code →](docs/automations-as-code.md) ·\n[Run outcomes and reliability limits →](docs/automation-runs.md)\n\n### Use every agent you pay for. Switch mid-task.\n\nRun Claude Code for one task, Codex for another, and Pi or OpenCode where they\nfit, side by side in one session list. Bivy supplies the shared session,\nremote-access, automation, and review surfaces; your chosen agent still does\nthe coding and uses your model provider.\n\n- **Hit a usage limit? Keep going.** When a turn fails on a limit, fork the\n session, with its code and conversation, to another agent and model, or let\n Bivy retry when the limit resets (the provider has to report a reset time).\n- **Get a second opinion.** `bivy delegate` hands a task to another agent, on\n the same machine or a different one, and brings back its answer, branch, and\n PR. `--to codex,claude@linux` sends one task to several agents to compare.\n- Choose an agent and, where supported, a model for each session or run.\n- Import existing Claude Code and Codex sessions.\n- Fork or move work to another agent or machine when a different setup fits\n better. Continuation fidelity varies: some paths preserve native history,\n while others replay portable turns or hand over a summary with the earlier\n transcript as a local file.\n- Use agent-native logins, Bivy-managed credentials, or local inference.\n Bivy's custom OpenAI-compatible endpoint registry currently feeds Pi;\n other agents may need their own provider configuration.\n- Register your own ACP or headless process agent with `bivy agent add`.\n\nBivy does not replace your agent, provide model inference, or make every agent's\nfeatures identical. Consult the [support matrix](docs/runtime-support-matrix.md)\nand [handoff recipes](docs/capability-recipes.md#fork-or-move-a-session).\n\n### Less signing in. Less copying secrets.\n\nBivy syncs **Bivy-managed API keys and supported OAuth credentials** across\nenrolled machines for compatible runtimes. Connect supported credentials once\nand reuse them where you run work, rather than manually distributing keys to\neach machine.\n\nFor ordinary account sync, credentials are encrypted on the node before upload.\nThe control plane stores ciphertext and wrapped-key metadata; enrolled nodes\nshare access by wrapping the vault key to one another. Bivy Cloud does not\nreceive plaintext credentials through this sync path.\n\nYou can also keep credentials local, use labeled keys and project presets, or\nreference environment variables and 1Password instead of embedding secrets in\nconfiguration:\n\n```bash\nbivy provider login\nbivy credentials add anthropic work\nbivy secrets ref github.repo-token op://Bivy/GitHub/repo-token\n```\n\n**Not every CLI login syncs.** Native agent logins may still be per-machine;\nGitHub App private-key sync is separately opt-in. If you lose every node and\ndevice able to unwrap a vault, you must sign in to providers again. Explicit\nhosted-provisioning custody grants are separate from ordinary encrypted sync.\n\n[Credential sync and runtime coverage →](docs/credential-sync.md) ·\n[Credentials guide →](docs/credentials-guide.md) ·\n[Key storage →](docs/key-management.md)\n\n### Work in the environment you already have\n\nA clean cloud sandbox isn't always enough. Your agent may need the database\nrunning on localhost, an uncommitted change, an internal API behind your VPN,\nor a model running on your GPU. Bivy runs the agent where those things already\nexist, subject to that machine's permissions and the runtime's protection.\n\nConnect several machines to the same account: a laptop for interactive work,\na Linux server for background jobs, or a GPU box for local inference. Choose\nthe machine for each session or pin it in an automation. Repository runs can\nuse isolated Git worktrees without rebuilding the whole development environment.\n\n**The execution machine must stay awake and online.** To close your laptop and\nleave work running, run the agent on a different, always-on machine.\n\n[Environment and multi-machine recipes →](docs/capability-recipes.md)\n\n### Start at your desk. Continue anywhere.\n\nOpen the same session in the browser, phone PWA, or terminal. Watch work live,\nanswer questions, approve supported tool calls, or stop the agent.\n\n- Send screenshots, images, logs, and other files from your phone.\n- Download reports and artifacts the agent creates.\n- Open [session apps](docs/apps.md) from chat or the **Apps** menu: live web\n previews, desktop app views, and CLI/TUI tools. Agents can publish views with\n `bivy app publish bivy.app.json` and create share links with `bivy app share`.\n- Use voice input and read-aloud where supported; provider-backed voice may\n send audio or text to the selected provider.\n- Keep a native terminal workflow or use structured chat, depending on the agent.\n\n```bash\nbivy run claude --no-follow # start without attaching\nbivy open # open the web app\nbivy resume # return to the session in your terminal\nbivy link # pair a device directly via QR\n```\n\nNo phone app installation is required. Open [app.bivy.sh](https://app.bivy.sh)\nin your browser; adding it to your home screen is optional.\n\n[Remote access →](docs/remote-access.md) ·\n[Voice, files, and terminal recipes →](docs/capability-recipes.md)\n\n### Agents can reach you, too\n\nEvery session gets the `bivy` CLI, so any agent with a shell can talk back\nthrough the app, not only agents with their own built-in tools:\n\n```bash\nbivy notify \"Tests are green, PR is up\" # chat card + phone push when you're away\nbivy ask \"Ship to staging?\" --option Yes --option No # waits for your answer\nbivy attach report.png --caption \"Before/after\" # show a file in the chat\nbivy suggest \"Add a dark theme to settings\" # a next task you start in one tap\nbivy context --json # session, workspace, machine, apps\n```\n\n`bivy help --json` lists every command, and with `BIVY_OUTPUT=json` failures\ncome back as structured errors with distinct exit codes, so an agent can tell\nwhat went wrong and what to run next. `bivy guide` prints short playbooks for\nagents, and the same commands are served as MCP tools for agents that prefer\nthem. Add your own standing instructions for\nevery session in **Settings → Agent instructions**.\n\n[Commands for agents inside a session →](docs/cli-reference.md#inside-an-agent-session) ·\n[Agent instructions →](docs/agent-instructions.md)\n\n## Get started\n\n### Install\n\nBivy supports **macOS and Linux with Node.js 20+**. The installer installs the\n`@bivy/bivy` package, runs guided setup, and starts a launchd or systemd service:\n\n```bash\ncurl -fsSL https://bivy.sh/install.sh | bash\n```\n\nSetup helps you choose an agent and configure remote access. Existing agents\nkeep their command, login, and configuration. The installer may use `sudo` to\ninstall a missing Node.js, but never for `npm install`. To inspect it first,\ndownload it with `curl -fsSL https://bivy.sh/install.sh -o install.sh`.\n\nAlready have Node.js and want to avoid sudo?\n\n```bash\nnpm install -g @bivy/bivy\nbivy setup\n```\n\nThen try one small task:\n\n```bash\ncd your-repo\nbivy run claude\n# Ask: \"Explain this repo and make one small, safe improvement. Run the relevant checks.\"\nbivy open\n```\n\nOpen that same session on your phone while it runs. For a web app, ask the agent\nto start its dev server, then open **Apps → Running in this workspace → Preview**.\nTry a page, point to something you'd change, and send the feedback back to the\nagent. No separate preview deployment needed.\n\nOnce that works, share a preview for review, connect another machine, or add\nyour first automation.\n\n**Local-only works too.** `bivy run`, `bivy resume`, and `bivy sessions` need no\naccount or server. Choose **local only for now** during setup; use `bivy login`\nlater.\n\n**Just your own devices?** `bivy tailscale` serves the app from the machine\nitself at `https://<machine>.<tailnet>.ts.net`, with no account, control plane\nor relay. You get the core over your tailnet: sessions, chat, approvals,\nquestions, terminals and files. Push notifications, app previews and share\nlinks need Bivy Cloud or a self-hosted server. See [Tailscale](docs/tailscale.md)\nand [Remote access](docs/remote-access.md).\n\n[Full quickstart →](docs/quickstart.md) ·\n[Installer options, service management, and uninstall →](docs/install.md)\n\n### Choose hosted or self-hosted\n\n| Option | What you get |\n|---|---|\n| **Free Cloud — $0** | Every launch feature, including automations; 10 new remote sessions per rolling seven days. No credit card required. |\n| **Cloud — $15/month** | The same features with unlimited remote sessions. |\n| **Self-hosted Core** | Operate the app, control plane, and relay yourself, with no Bivy usage limits. |\n\nManual and automated sessions share the Cloud allowance. Resuming existing\nsessions and viewing history do not consume it. Agent subscriptions and model\nprovider charges are separate. See [current pricing](https://bivy.sh#pricing).\n\n**Self-host anywhere:** deploy the public control-plane (including the web app)\nand relay images with Postgres and [a small set of environment variables](docs/deploy-images.md).\nYour server or container platform handles HTTPS. Set up owner access in the\nbrowser—no SSH, external authentication provider, or Bivy Cloud account required.\nFor a bare VPS, the [Compose installer](docs/self-host-quickstart.md) automates the\nsame stack. These onboarding features require a release containing them.\n\nStart on Cloud and self-host later if you prefer. Deploy the stack, reconnect\nmachines with `bivy relay:setup`, and pair devices to your server. This is not a\none-click migration of your Cloud account; your local repos and agent\nconfiguration stay in place.\n\n```bash\nbivy relay:setup \\\n --control-plane https://bivy.example.com \\\n --relay wss://relay.example.com\n```\n\nSelf-hosting is community-supported: you own TLS, backups, upgrades, and\nhardening. Public multi-architecture images are available as\n`ghcr.io/bivysh/bivy-control-plane` and `ghcr.io/bivysh/bivy-relay`; pin a release\nversion or full commit SHA.\n\n[Deploy the images anywhere →](docs/deploy-images.md) ·\n[Optional VPS installer →](docs/self-host-quickstart.md) ·\n[Operations reference →](docs/self-host.md)\n\n## Architecture\n\nYour environment, with clear security boundaries:\n\n```text\nYour machine Hosted or self-hosted\n┌──────────────────────┐ ┌──────────────────────┐\n│ Node daemon │──outbound──▶│ Relay │\n│ Agents, repos, tools │ │ Encrypted frames │\n│ Local credentials │ └──────────┬───────────┘\n└──────────────────────┘ │\n Browser / phone\n + control plane\n (app, accounts, metadata)\n```\n\n- **Execution stays on your machine.** Bivy Cloud does not run your agents.\n Your model provider still sees whatever the agent sends it.\n- **Interactive session traffic is end-to-end encrypted** between the node and\n paired devices. The relay forwards opaque session frames; your node dials out,\n so no inbound public port is required.\n- **App previews have a separate security boundary.** Preview traffic uses HTTPS\n and an outbound tunnel, not session E2E encryption. The preview ingress/relay\n operator can see it. Public preview links grant anyone holding them access to\n that view and its live backend until expiry or revocation. See\n [preview security and sharing](docs/apps.md#runtime-and-security-boundaries).\n- **Ordinary credential sync uploads ciphertext, not plaintext keys.**\n Supported credentials and recovery limits are documented separately.\n- **Encryption is not universal across integrations.** Slack commands and\n generic webhook instructions reach the control plane in plaintext. Do not\n put secrets in them. Routing and bounded run metadata are also visible there.\n- **Device authorization matters.** QR pairing authorizes a device directly\n through the node. Hosted account pairing trusts the control plane to authorize\n devices and serve the web app that holds client keys.\n- **Bivy is not an OS-level sandbox.** The default approval mode is\n `autonomous`; protection depends on the runtime. Some agents enforce sandbox\n tiers, while process agents may run with your full user permissions.\n Heuristic tool checks help prevent accidents but are not isolation.\n\nReview the runtime's Protection label and configure approval/sandbox settings\nfor the task, especially before enabling unattended work.\n\n[Security model and known limitations →](docs/security-model.md) ·\n[Runtime protection matrix →](docs/runtime-support-matrix.md) ·\n[Configuration →](docs/configuration.md)\n\n## Agents and everyday commands\n\n**Claude Code, Codex, Pi, OpenCode, and Grok are release-tested.** Additional\nadapters include Gemini CLI, Qwen Code, Goose, Aider, Cline, Crush, Cursor, GitHub\nCopilot, Amp, Auggie, Droid, Continue, Kilo Code, and Rovo Dev. Installation,\nresume, model selection, and tool protection vary—see the\n[support matrix](docs/runtime-support-matrix.md) and [agent guides](docs/agents/README.md).\n\nRun an arbitrary command with `bivy run -- ./your-agent --flags`, register a\nreusable entry with `bivy agent add`, or package a declarative integration with\nexperimental [plugins](docs/plugins.md).\n\n```bash\nbivy run claude # launch a durable session; also codex, pi, opencode\nbivy sessions # list live and saved sessions\nbivy resume # resume the most recent session\nbivy open # open the web app (requires remote setup)\nbivy nodes # list connected account machines\nbivy runs list # inspect delegated work\nbivy automation init # scaffold repo-owned automations\nbivy provider login # connect supported model credentials\nbivy agent add # register an ACP or process agent\nbivy doctor # check installation and connectivity\nbivy logs -f # follow node logs\nbivy update # update and restart the service\n```\n\n`bivy update` uses your original installation method and waits for an active\nturn to finish before restarting. Use `--force` to skip that wait.\n\n[CLI reference →](docs/cli-reference.md) ·\n[Node and project configuration →](docs/config-as-code.md) ·\n[GitHub setup →](docs/github-setup.md) ·\n[Linear setup →](docs/linear-work-queue.md)\n\n## Development and contributions\n\n```bash\npnpm install\npnpm run dev # node daemon on http://localhost:4317\npnpm run dev:web # web client dev server\n```\n\n| Directory | Contents |\n|---|---|\n| `src/`, `bin/` | Node daemon, CLI, runtime adapters, sessions, approvals, secrets |\n| `packages/core/` | Shared protocol, pairing, and wire format |\n| `packages/web/`, `packages/ui/` | React PWA and shared design system |\n| `services/relay/` | Self-hostable encrypted relay |\n| `services/control-plane/` | Self-hostable control plane |\n| `deploy/` | Deployment examples |\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\nSee [CONTRIBUTING.md](CONTRIBUTING.md) for the development workflow. Releases\nare published from CI with provenance attestations; see\n[release verification](docs/releasing.md).\n\n**Found a security issue?** Use\n[GitHub private vulnerability reporting](https://github.com/bivysh/bivy/security/advisories/new),\nnot a public issue. See [SECURITY.md](SECURITY.md).\n\n### In development—not available at launch\n\nAutomatically provisioned, short-lived **ephemeral machines** are in development\nfor hosted and bring-your-own-cloud deployments. Neither path is ready or\nsupported for this launch. Use an existing computer or server you operate.\nExperimental provisioning has different credential-custody and encryption\nboundaries; see the [provisioning trust model](docs/hosted-provisioning-trust-model.md).\n\n## License\n\nEverything in this repository—node, CLI, web/PWA, relay, and control plane—is\nfree and open-source **AGPL-3.0-only Core**, with no Bivy usage limits. You may\nuse, modify, and self-host it under that license. If users interact with your\nmodified version over a network, section 13 requires you to offer its\ncorresponding source. See [LICENSE](LICENSE).\n\nBivy Cloud is the hosted operation of that stack. Its plans, billing and\nmanaged-compute operations live in a separate private service behind Core's\ndeployment extension. Contributions are made under the\n[Contributor License Agreement](CLA.md). The Bivy name and logo are covered by\nthe [trademark policy](TRADEMARKS.md).\n",
68
+ "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**Local coding agents that show their work. Anywhere.**\n\nBivy is the open-source workspace for coding agents. It runs Claude Code, Codex,\nPi, OpenCode and other agents on your own machine. Reach them from a browser or\nyour phone, with the live session, its terminal, its approvals, and the app\nthey're building right in the chat. Try it, mark what's wrong, and send it back.\n\n<p align=\"center\">\n <img src=\"docs/images/preview-feedback-loop.gif\" width=\"360\"\n alt=\"On a phone: open the app an agent built, circle the packed items, send a note, and compare the result before and after the agent's fix.\">\n</p>\n\nBivy is not another coding agent and not a cloud development machine. The agent\nstays local and does the coding with your model provider. Your repos, tools,\ndatabases, and services stay where they are. Bivy gives you remote access to all\nof it, plus live previews, automations, and review.\n\n- **Live previews.** The running app beside the chat. Mark it, send it back,\n share it with someone who has no Bivy account.\n- **Automations.** GitHub issues, failed CI, Linear, Slack, schedules, and\n webhooks start the work. It comes back as a preview link or a pull request.\n- **Any agent.** Hit a usage limit? Fork the session to another agent, or let\n Bivy retry when the limit resets.\n- **Your machine, anywhere.** Start at your desk, pick the session up on your\n phone. The machine dials out, so there are no ports to open, and session\n traffic is end-to-end encrypted.\n\n**[Start free on Bivy Cloud](https://app.bivy.sh)** ·\n**[Quickstart](docs/quickstart.md)** ·\n**[Documentation](docs/README.md)** ·\n**[Self-host](docs/deploy-images.md)** ·\n**[Website](https://bivy.sh)**\n\n**Recommended:** sign in at [app.bivy.sh](https://app.bivy.sh), then copy your\npersonalized **Connect a Machine** command into a terminal on your Mac or Linux\ncomputer. It installs and enrolls the machine without another Bivy login. The\nbrowser connects automatically; choose a repository and send your first task\nright there.\n\nPrefer starting from the terminal?\n\n```bash\ncurl -fsSL https://bivy.sh/install.sh | bash # install + guided setup\ncd your-repo\nbivy run claude # or codex, pi, opencode\nbivy open # continue in the web app (needs remote setup)\n```\n\nBivy Cloud hosts the app, control plane, and relay—not the machines running your\nagents. Connect a Mac, Linux computer, or existing server and bring your own\nagent subscription, model API key, or local model. You can also self-host the\nentire remote-access stack.\n\n> **Bivy is 0.x software.** Claude Code, Codex, Pi, OpenCode, and Grok are the\n> release-tested paths. Credential sync, resume, handoffs, approvals, and\n> sandboxing depend on the runtime. See the\n> [runtime support matrix](docs/runtime-support-matrix.md).\n\n## Don't just read the diff. Try the app.\n\nBivy lets you **use what the agent built and show it what to fix**, without a\nseparate deployment step.\n\n1. **Ask for a change.** The agent works in your repo with your existing tools\n and local services.\n2. **Try the app.** Bivy finds the dev server the agent starts and opens it\n beside the chat: web apps, terminals, and desktop apps. Use the running app,\n not a screenshot of what the agent says it finished.\n3. **Mark what needs work.** Press and hold anything in the preview to mark it,\n or drag to circle it, then say what you want. **Mark another** keeps\n separate notes separate. The marks go to the agent with the element and page\n context. No screenshots to paste.\n4. **Follow each note.** Every mark stays in the chat as a pin that resolves\n itself: **Changed** when a later run changes what you marked, **Element\n gone** when it leaves the page, **Done** when you say so.\n5. **Review the next version.** A new version waits on the preview pill instead\n of reloading under you. Take it when you're ready, compare before and after,\n inspect the diff and checks, and decide when the work is done.\n6. **Get a second opinion.** Share a preview link for 1 hour, 1 day, or 7 days.\n Teammates and clients try the running app and leave notes without a Bivy\n account. Make it view-only, or **Stop sharing** to end every link at once.\n\nOn Bivy Cloud, preview delivery is built in: no per-app domains, certificates,\npublic ports, or tunnels to configure. Self-hosters configure preview delivery\nonce for their Bivy deployment, not for every app. The app still needs its normal\nbuild or dev-server setup, and **the machine serving it must stay awake and\nonline**. These are development previews, not production hosting.\n\n**Share deliberately:** anyone with a preview link can use that app, including\nits live backend, until the link expires or you stop sharing.\nReviewer notes aren't sent to the agent automatically; you send them or explicitly\nallow agent access. Preview traffic uses HTTPS through the preview relay, not\nsession end-to-end encryption; the relay operator can see it.\n\n[App previews, visual feedback, and sharing →](docs/apps.md)\n\n## One workflow, from trigger to review\n\n```text\nPrompt · GitHub issue · CI failure · Linear · Slack · Schedule · Webhook\n │\n ▼\n Choose machine + agent + model\n + supported credentials\n │\n ▼\n Live agent session\n Join · steer · approve · stop\n │\n ▼\n Try app · inspect changes · checks\n │\n ▼\n Mark up · send feedback · iterate\n │\n ▼\n Share preview · review PR\n```\n\nA **Machine** is a computer or server you connect. A **Session** is live agent\nwork on that machine. A **Run** is delegated background work that creates a\nsession and tracks its outcome. An **Automation** is a reusable definition that\ncreates runs when an event matches.\n\nManual and automated work use the same kind of live session. You can join a run\nwhen it needs help rather than wait for a black-box job to finish.\n\n### Let events start the work\n\nAutomations turn recurring or incoming work into sessions you can join,\nsupervise, and review. An agent picks the task up on your machine and posts\nback a preview link or a pull request, so you try the result where the task\nlives and reply in the same session. Choose the repository, machine, agent,\nmodel, approval mode, sandbox setting, and maximum attempts.\n\n| Trigger | Example workflow |\n|---|---|\n| **GitHub issues and mentions** | Label an issue `bivy` or `bivy/<machine>`, or mention your Bivy GitHub App, to work toward a pull request. |\n| **Failed CI** | Match a failed workflow, ask the agent to reproduce it, make a fix, and run the affected checks. |\n| **Linear** | Label an issue to start work without copying its description into an agent. |\n| **Slack** | Send a request from the conversation where the work came up. |\n| **Schedules** | Run a weekly dependency review, recurring maintenance, or a one-time task. |\n| **Signed webhooks** | Connect alerts, internal tools, or your own event sources. |\n\nConfigure automations in the app or version them with your repository in\n`.bivy/automations.yaml`:\n\n```bash\nbivy automation init\n# Edit the generated definition for your repository and workflow.\nbivy automation validate\nbivy automation test --event .bivy/events/failed-ci.yaml # supply a local event fixture\nbivy automation apply\n```\n\nOr delegate a one-off job without creating an automation:\n\n```bash\nbivy runs start \"Review outdated dependencies and propose a small, tested update.\"\nbivy runs wait <id>\n```\n\nRuns keep routing and lifecycle evidence, check results, and output references\nin a reviewable Receipt. For unattended issue work, Bivy runs declared repository\nchecks after the agent's turn; failed required checks fail the run even if the\nagent reports success. A completed process alone is not proof that the task\nsucceeded.\n\n[Automation recipes →](docs/capability-recipes.md#let-events-start-runs) ·\n[Automations as code →](docs/automations-as-code.md) ·\n[Run outcomes and reliability limits →](docs/automation-runs.md)\n\n### Use every agent you pay for. Switch mid-task.\n\nRun Claude Code for one task, Codex for another, and Pi or OpenCode where they\nfit, side by side in one session list. Bivy supplies the shared session,\nremote-access, automation, and review surfaces; your chosen agent still does\nthe coding and uses your model provider.\n\n- **Hit a usage limit? Keep going.** When a turn fails on a limit, fork the\n session, with its code and conversation, to another agent and model, or let\n Bivy retry when the limit resets (the provider has to report a reset time).\n- **Get a second opinion.** `bivy delegate` hands a task to another agent, on\n the same machine or a different one, and brings back its answer, branch, and\n PR. `--to codex,claude@linux` sends one task to several agents to compare.\n- Choose an agent and, where supported, a model for each session or run.\n- Import existing Claude Code and Codex sessions.\n- Fork or move work to another agent or machine when a different setup fits\n better. Continuation fidelity varies: some paths preserve native history,\n while others replay portable turns or hand over a summary with the earlier\n transcript as a local file.\n- Use agent-native logins, Bivy-managed credentials, or local inference.\n Bivy's custom OpenAI-compatible endpoint registry currently feeds Pi;\n other agents may need their own provider configuration.\n- Register your own ACP or headless process agent with `bivy agent add`.\n\nBivy does not replace your agent, provide model inference, or make every agent's\nfeatures identical. Consult the [support matrix](docs/runtime-support-matrix.md)\nand [handoff recipes](docs/capability-recipes.md#fork-or-move-a-session).\n\n### Less signing in. Less copying secrets.\n\nBivy syncs **Bivy-managed API keys and supported OAuth credentials** across\nenrolled machines for compatible runtimes. Connect supported credentials once\nand reuse them where you run work, rather than manually distributing keys to\neach machine.\n\nFor ordinary account sync, credentials are encrypted on the node before upload.\nThe control plane stores ciphertext and wrapped-key metadata; enrolled nodes\nshare access by wrapping the vault key to one another. Bivy Cloud does not\nreceive plaintext credentials through this sync path.\n\nYou can also keep credentials local, use labeled keys and project presets, or\nreference environment variables and 1Password instead of embedding secrets in\nconfiguration:\n\n```bash\nbivy provider login\nbivy credentials add anthropic work\nbivy secrets ref github.repo-token op://Bivy/GitHub/repo-token\n```\n\n**Not every CLI login syncs.** Native agent logins may still be per-machine;\nGitHub App private-key sync is separately opt-in. If you lose every node and\ndevice able to unwrap a vault, you must sign in to providers again. Explicit\nhosted-provisioning custody grants are separate from ordinary encrypted sync.\n\n[Credential sync and runtime coverage →](docs/credential-sync.md) ·\n[Credentials guide →](docs/credentials-guide.md) ·\n[Key storage →](docs/key-management.md)\n\n### Work in the environment you already have\n\nA clean cloud sandbox isn't always enough. Your agent may need the database\nrunning on localhost, an uncommitted change, an internal API behind your VPN,\nor a model running on your GPU. Bivy runs the agent where those things already\nexist, subject to that machine's permissions and the runtime's protection.\n\nConnect several machines to the same account: a laptop for interactive work,\na Linux server for background jobs, or a GPU box for local inference. Choose\nthe machine for each session or pin it in an automation. Repository runs can\nuse isolated Git worktrees without rebuilding the whole development environment.\n\n**The execution machine must stay awake and online.** To close your laptop and\nleave work running, run the agent on a different, always-on machine.\n\n[Environment and multi-machine recipes →](docs/capability-recipes.md)\n\n### Start at your desk. Continue anywhere.\n\nOpen the same session in the browser, phone PWA, or terminal. Watch work live,\nanswer questions, approve supported tool calls, or stop the agent.\n\n- Send screenshots, images, logs, and other files from your phone.\n- Download reports and artifacts the agent creates.\n- Open [session apps](docs/apps.md) from chat or the **Apps** menu: live web\n previews, desktop app views, and CLI/TUI tools. Agents can publish views with\n `bivy app publish bivy.app.json` and create share links with `bivy app share`.\n- Use voice input and read-aloud where supported; provider-backed voice may\n send audio or text to the selected provider.\n- Keep a native terminal workflow or use structured chat, depending on the agent.\n\n```bash\nbivy run claude --no-follow # start without attaching\nbivy open # open the web app\nbivy resume # return to the session in your terminal\nbivy link # pair a device directly via QR\n```\n\nNo phone app installation is required. Open [app.bivy.sh](https://app.bivy.sh)\nin your browser; adding it to your home screen is optional.\n\n[Remote access →](docs/remote-access.md) ·\n[Voice, files, and terminal recipes →](docs/capability-recipes.md)\n\n### Agents can reach you, too\n\nEvery session gets the `bivy` CLI, so any agent with a shell can talk back\nthrough the app, not only agents with their own built-in tools:\n\n```bash\nbivy notify # push to your phone so you come back\nbivy ask \"Ship to staging?\" --option Yes --option No # waits for your answer\nbivy attach report.png --caption \"Before/after\" # show a file in the chat\nbivy context --json # session, workspace, machine, apps\n```\n\n`bivy help --json` lists every command, and with `BIVY_OUTPUT=json` failures\ncome back as structured errors with distinct exit codes, so an agent can tell\nwhat went wrong and what to run next. `bivy guide` prints short playbooks for\nagents, and the same commands are served as MCP tools for agents that prefer\nthem. Add your own standing instructions for\nevery session in **Settings → Agent instructions**.\n\n[Commands for agents inside a session →](docs/cli-reference.md#inside-an-agent-session) ·\n[Agent instructions →](docs/agent-instructions.md)\n\n## Get started\n\n### Install\n\nBivy supports **macOS and Linux with Node.js 20+**. The installer installs the\n`@bivy/bivy` package, runs guided setup, and starts a launchd or systemd service:\n\n```bash\ncurl -fsSL https://bivy.sh/install.sh | bash\n```\n\nSetup helps you choose an agent and configure remote access. Existing agents\nkeep their command, login, and configuration. The installer may use `sudo` to\ninstall a missing Node.js, but never for `npm install`. To inspect it first,\ndownload it with `curl -fsSL https://bivy.sh/install.sh -o install.sh`.\n\nAlready have Node.js and want to avoid sudo?\n\n```bash\nnpm install -g @bivy/bivy\nbivy setup\n```\n\nThen try one small task:\n\n```bash\ncd your-repo\nbivy run claude\n# Ask: \"Explain this repo and make one small, safe improvement. Run the relevant checks.\"\nbivy open\n```\n\nOpen that same session on your phone while it runs. For a web app, ask the agent\nto start its dev server, then open **Apps → Running in this workspace → Preview**.\nTry a page, point to something you'd change, and send the feedback back to the\nagent. No separate preview deployment needed.\n\nOnce that works, share a preview for review, connect another machine, or add\nyour first automation.\n\n**Local-only works too.** `bivy run`, `bivy resume`, and `bivy sessions` need no\naccount or server. Choose **local only for now** during setup; use `bivy login`\nlater.\n\n**Just your own devices?** `bivy tailscale` serves the app from the machine\nitself at `https://<machine>.<tailnet>.ts.net`, with no account, control plane\nor relay. You get the core over your tailnet: sessions, chat, approvals,\nquestions, terminals and files. Push notifications, app previews and share\nlinks need Bivy Cloud or a self-hosted server. See [Tailscale](docs/tailscale.md)\nand [Remote access](docs/remote-access.md).\n\n[Full quickstart →](docs/quickstart.md) ·\n[Installer options, service management, and uninstall →](docs/install.md)\n\n### Choose hosted or self-hosted\n\n| Option | What you get |\n|---|---|\n| **Free Cloud — $0** | Every launch feature, including automations; 10 new remote sessions per rolling seven days. No credit card required. |\n| **Cloud — $15/month** | The same features with unlimited remote sessions. |\n| **Self-hosted Core** | Operate the app, control plane, and relay yourself, with no Bivy usage limits. |\n\nManual and automated sessions share the Cloud allowance. Resuming existing\nsessions and viewing history do not consume it. Agent subscriptions and model\nprovider charges are separate. See [current pricing](https://bivy.sh#pricing).\n\n**Self-host anywhere:** deploy the public control-plane (including the web app)\nand relay images with Postgres and [a small set of environment variables](docs/deploy-images.md).\nYour server or container platform handles HTTPS. Set up owner access in the\nbrowser—no SSH, external authentication provider, or Bivy Cloud account required.\nFor a bare VPS, the [Compose installer](docs/self-host-quickstart.md) automates the\nsame stack. These onboarding features require a release containing them.\n\nStart on Cloud and self-host later if you prefer. Deploy the stack, reconnect\nmachines with `bivy relay:setup`, and pair devices to your server. This is not a\none-click migration of your Cloud account; your local repos and agent\nconfiguration stay in place.\n\n```bash\nbivy relay:setup \\\n --control-plane https://bivy.example.com \\\n --relay wss://relay.example.com\n```\n\nSelf-hosting is community-supported: you own TLS, backups, upgrades, and\nhardening. Public multi-architecture images are available as\n`ghcr.io/bivysh/bivy-control-plane` and `ghcr.io/bivysh/bivy-relay`; pin a release\nversion or full commit SHA.\n\n[Deploy the images anywhere →](docs/deploy-images.md) ·\n[Optional VPS installer →](docs/self-host-quickstart.md) ·\n[Operations reference →](docs/self-host.md)\n\n## Architecture\n\nYour environment, with clear security boundaries:\n\n```text\nYour machine Hosted or self-hosted\n┌──────────────────────┐ ┌──────────────────────┐\n│ Node daemon │──outbound──▶│ Relay │\n│ Agents, repos, tools │ │ Encrypted frames │\n│ Local credentials │ └──────────┬───────────┘\n└──────────────────────┘ │\n Browser / phone\n + control plane\n (app, accounts, metadata)\n```\n\n- **Execution stays on your machine.** Bivy Cloud does not run your agents.\n Your model provider still sees whatever the agent sends it.\n- **Interactive session traffic is end-to-end encrypted** between the node and\n paired devices. The relay forwards opaque session frames; your node dials out,\n so no inbound public port is required.\n- **App previews have a separate security boundary.** Preview traffic uses HTTPS\n and an outbound tunnel, not session E2E encryption. The preview ingress/relay\n operator can see it. Public preview links grant anyone holding them access to\n that view and its live backend until expiry or revocation. See\n [preview security and sharing](docs/apps.md#runtime-and-security-boundaries).\n- **Ordinary credential sync uploads ciphertext, not plaintext keys.**\n Supported credentials and recovery limits are documented separately.\n- **Encryption is not universal across integrations.** Slack commands and\n generic webhook instructions reach the control plane in plaintext. Do not\n put secrets in them. Routing and bounded run metadata are also visible there.\n- **Device authorization matters.** QR pairing authorizes a device directly\n through the node. Hosted account pairing trusts the control plane to authorize\n devices and serve the web app that holds client keys.\n- **Bivy is not an OS-level sandbox.** The default approval mode is\n `autonomous`; protection depends on the runtime. Some agents enforce sandbox\n tiers, while process agents may run with your full user permissions.\n Heuristic tool checks help prevent accidents but are not isolation.\n\nReview the runtime's Protection label and configure approval/sandbox settings\nfor the task, especially before enabling unattended work.\n\n[Security model and known limitations →](docs/security-model.md) ·\n[Runtime protection matrix →](docs/runtime-support-matrix.md) ·\n[Configuration →](docs/configuration.md)\n\n## Agents and everyday commands\n\n**Claude Code, Codex, Pi, OpenCode, and Grok are release-tested.** Additional\nadapters include Gemini CLI, Qwen Code, Goose, Aider, Cline, Crush, Cursor, GitHub\nCopilot, Amp, Auggie, Droid, Continue, Kilo Code, and Rovo Dev. Installation,\nresume, model selection, and tool protection vary—see the\n[support matrix](docs/runtime-support-matrix.md) and [agent guides](docs/agents/README.md).\n\nRun an arbitrary command with `bivy run -- ./your-agent --flags`, register a\nreusable entry with `bivy agent add`, or package a declarative integration with\nexperimental [plugins](docs/plugins.md).\n\n```bash\nbivy run claude # launch a durable session; also codex, pi, opencode\nbivy sessions # list live and saved sessions\nbivy resume # resume the most recent session\nbivy open # open the web app (requires remote setup)\nbivy nodes # list connected account machines\nbivy runs list # inspect delegated work\nbivy automation init # scaffold repo-owned automations\nbivy provider login # connect supported model credentials\nbivy agent add # register an ACP or process agent\nbivy doctor # check installation and connectivity\nbivy logs -f # follow node logs\nbivy update # update and restart the service\n```\n\n`bivy update` uses your original installation method and waits for an active\nturn to finish before restarting. Use `--force` to skip that wait.\n\n[CLI reference →](docs/cli-reference.md) ·\n[Node and project configuration →](docs/config-as-code.md) ·\n[GitHub setup →](docs/github-setup.md) ·\n[Linear setup →](docs/linear-work-queue.md)\n\n## Development and contributions\n\n```bash\npnpm install\npnpm run dev # node daemon on http://localhost:4317\npnpm run dev:web # web client dev server\n```\n\n| Directory | Contents |\n|---|---|\n| `src/`, `bin/` | Node daemon, CLI, runtime adapters, sessions, approvals, secrets |\n| `packages/core/` | Shared protocol, pairing, and wire format |\n| `packages/web/`, `packages/ui/` | React PWA and shared design system |\n| `services/relay/` | Self-hostable encrypted relay |\n| `services/control-plane/` | Self-hostable control plane |\n| `deploy/` | Deployment examples |\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\nSee [CONTRIBUTING.md](CONTRIBUTING.md) for the development workflow. Releases\nare published from CI with provenance attestations; see\n[release verification](docs/releasing.md).\n\n**Found a security issue?** Use\n[GitHub private vulnerability reporting](https://github.com/bivysh/bivy/security/advisories/new),\nnot a public issue. See [SECURITY.md](SECURITY.md).\n\n### In development—not available at launch\n\nAutomatically provisioned, short-lived **ephemeral machines** are in development\nfor hosted and bring-your-own-cloud deployments. Neither path is ready or\nsupported for this launch. Use an existing computer or server you operate.\nExperimental provisioning has different credential-custody and encryption\nboundaries; see the [provisioning trust model](docs/hosted-provisioning-trust-model.md).\n\n## License\n\nEverything in this repository—node, CLI, web/PWA, relay, and control plane—is\nfree and open-source **AGPL-3.0-only Core**, with no Bivy usage limits. You may\nuse, modify, and self-host it under that license. If users interact with your\nmodified version over a network, section 13 requires you to offer its\ncorresponding source. See [LICENSE](LICENSE).\n\nBivy Cloud is the hosted operation of that stack. Its plans, billing and\nmanaged-compute operations live in a separate private service behind Core's\ndeployment extension. Contributions are made under the\n[Contributor License Agreement](CLA.md). The Bivy name and logo are covered by\nthe [trademark policy](TRADEMARKS.md).\n",
69
69
  "readmeFilename": "README.md",
70
70
  "agentBridges": {
71
71
  "@anthropic-ai/claude-agent-sdk": "0.3.286",