@bivy/bivy 0.21.1 → 0.21.2-staging.10

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 (122) hide show
  1. package/bin/archive-install.mjs +150 -0
  2. package/bin/bivy.mjs +66 -7
  3. package/bin/cli-commands.mjs +1 -1
  4. package/bin/guides/add-an-agent.md +39 -0
  5. package/bin/prune-sessions.mjs +46 -14
  6. package/dist/agent-cli.js +91 -63
  7. package/dist/agents/claude-code/runtime.js +19 -10
  8. package/dist/apps/styles.css +1 -50
  9. package/dist/automation-reseal.js +2 -0
  10. package/dist/node-config.js +18 -0
  11. package/dist/node-update.js +26 -1
  12. package/dist/plugin-cli.js +3 -4
  13. package/dist/plugin-sdk/manifest.js +19 -11
  14. package/dist/runtime/cli-parsers.js +18 -1
  15. package/dist/runtime/index.js +71 -41
  16. package/dist/server.js +38 -34
  17. package/dist/worktree.js +17 -0
  18. package/package.json +1 -1
  19. package/web/assets/AutomationsView-DRwMTpsz.js +51 -0
  20. package/web/assets/{ChangesCard-CxQKGf6b.js → ChangesCard-C5rvJ8Cq.js} +3 -3
  21. package/web/assets/{LibraryView-D3uu06xU.js → LibraryView-CIJ-Pfv-.js} +1 -1
  22. package/web/assets/ProviderConnect-DiFSOT6L.js +1 -0
  23. package/web/assets/SessionList-CxRibKT5.js +1 -0
  24. package/web/assets/{Settings-DrK5DKuy.js → Settings-BukJBTwA.js} +8 -8
  25. package/web/assets/{Sheet-C8gx0OoI.js → Sheet-CfbcQeWy.js} +1 -1
  26. package/web/assets/{Terminal-DrqMIIhr.js → Terminal-7Q0Os9n9.js} +5 -5
  27. package/web/assets/{VoiceSettings-CBBuyKN7.js → VoiceSettings-DwIi92pR.js} +1 -1
  28. package/web/assets/{abnfDiagram-VCTEODGH-CgIjZZoL.js → abnfDiagram-VCTEODGH-Czjfvfve.js} +1 -1
  29. package/web/assets/architecture-7GRP2DOG-DD5qRKOE.js +1 -0
  30. package/web/assets/{architectureDiagram-5GKGNRK7-BMOlU5hT.js → architectureDiagram-5GKGNRK7-DYay_IWI.js} +1 -1
  31. package/web/assets/{blockDiagram-I7D4REHJ-DkwymH1p.js → blockDiagram-I7D4REHJ-CPlmINAT.js} +1 -1
  32. package/web/assets/{c4Diagram-7LVT6UL2-B0UDgtWS.js → c4Diagram-7LVT6UL2-DYcLdJNN.js} +1 -1
  33. package/web/assets/channel-BUg-Y_4E.js +1 -0
  34. package/web/assets/{chunk-4HAMMTFA-1y76NzcS.js → chunk-4HAMMTFA-CdyB2GrZ.js} +1 -1
  35. package/web/assets/{chunk-75Z2AOVW-CPFkG_FO.js → chunk-75Z2AOVW-D7h9tbn8.js} +1 -1
  36. package/web/assets/{chunk-DU6HZSFF-Dy4LQ-iK.js → chunk-DU6HZSFF-BISdfBlT.js} +1 -1
  37. package/web/assets/{chunk-F27PBJKO-cjpOzWOK.js → chunk-F27PBJKO-2JkB2zU9.js} +1 -1
  38. package/web/assets/{chunk-GMAD6QVW-CpYC8bxK.js → chunk-GMAD6QVW-BfxS2p5H.js} +1 -1
  39. package/web/assets/{chunk-GVQU2GXP-DUT271z8.js → chunk-GVQU2GXP-vBPS78dh.js} +1 -1
  40. package/web/assets/{chunk-IMKFNOWR-C-G3w5yT.js → chunk-IMKFNOWR-C1PvbEPt.js} +1 -1
  41. package/web/assets/{chunk-L3NEJ4N5-C5N0po6u.js → chunk-L3NEJ4N5-wug0ZtiI.js} +1 -1
  42. package/web/assets/{chunk-OSK3NFVY-7_vZDCyQ.js → chunk-OSK3NFVY-CSx22WTp.js} +1 -1
  43. package/web/assets/{chunk-P2QGCYS3-BhXxw6WP.js → chunk-P2QGCYS3-BEJVL1Uw.js} +1 -1
  44. package/web/assets/{chunk-POPQ4Y6H-iw5O0upC.js → chunk-POPQ4Y6H-DuEsyMhL.js} +1 -1
  45. package/web/assets/{chunk-PWAF6VOD-C8u0Giuo.js → chunk-PWAF6VOD-DVLOKu0r.js} +1 -1
  46. package/web/assets/{chunk-SHT3W25Y-BM_QeCu-.js → chunk-SHT3W25Y-Opayr0C_.js} +1 -1
  47. package/web/assets/{chunk-SVP7TREG-cLDkReFI.js → chunk-SVP7TREG-DgHJUbyu.js} +1 -1
  48. package/web/assets/{chunk-TICWLB2K-C8fbKbO_.js → chunk-TICWLB2K-DOOCLkvY.js} +1 -1
  49. package/web/assets/classDiagram-ZZMXUADV-DPpdYhAI.js +1 -0
  50. package/web/assets/classDiagram-v2-VYDZK3BY-DPpdYhAI.js +1 -0
  51. package/web/assets/{cynefin-OW5HDTMX-6kzYNsQ0.js → cynefin-OW5HDTMX-1MBj3btC.js} +1 -1
  52. package/web/assets/{cynefinDiagram-5FMLGOSQ-B3tl5cyI.js → cynefinDiagram-5FMLGOSQ-Cc6rrG7K.js} +1 -1
  53. package/web/assets/{dagre-GXQ25YYZ-mHX4cPQQ.js → dagre-GXQ25YYZ-DFG3Ndtk.js} +1 -1
  54. package/web/assets/{diagram-S7CK7UJ4-DhOtHdTh.js → diagram-S7CK7UJ4-SiM6_pqS.js} +1 -1
  55. package/web/assets/{diagram-UQ7AKVKN-B6BD-P5q.js → diagram-UQ7AKVKN-BUI0Ju7Z.js} +1 -1
  56. package/web/assets/{diagram-VSXAHHWV-DWXWifeJ.js → diagram-VSXAHHWV-CXMYZkbm.js} +1 -1
  57. package/web/assets/{diagram-VX7I27RA-DcU0vblN.js → diagram-VX7I27RA-CNCbmY76.js} +1 -1
  58. package/web/assets/{diagram-Z3DM3KII-Tc32VaTG.js → diagram-Z3DM3KII-Cf3mrdyV.js} +1 -1
  59. package/web/assets/{ebnfDiagram-PWID7BFC-fbSC7Ytl.js → ebnfDiagram-PWID7BFC-Ban7TN8N.js} +1 -1
  60. package/web/assets/{erDiagram-RLTQ6QDP-VCd4vHPL.js → erDiagram-RLTQ6QDP-yDvXNUaN.js} +1 -1
  61. package/web/assets/eventmodeling-NTZA5JFV-BktxSOvh.js +1 -0
  62. package/web/assets/flowDiagram-HODETNUW-CuT_k3cJ.js +1 -0
  63. package/web/assets/{ganttDiagram-EL5Y4UJY-qlPJPHC9.js → ganttDiagram-EL5Y4UJY-CRtk9R_T.js} +1 -1
  64. package/web/assets/{gitGraph-4MIJSDKK-Frn6_UlZ.js → gitGraph-4MIJSDKK-DVCE-pa5.js} +1 -1
  65. package/web/assets/{gitGraphDiagram-WWUBYQGX-CJv1U3iy.js → gitGraphDiagram-WWUBYQGX-BGNEoPtQ.js} +1 -1
  66. package/web/assets/{index-DoVsvSyc.js → index-Bkcd452U.js} +2 -2
  67. package/web/assets/index-CkoStrAe.css +1 -0
  68. package/web/assets/{info-A6RAGUB7-x2ehGecF.js → info-A6RAGUB7-CmlSnIUY.js} +1 -1
  69. package/web/assets/{infoDiagram-27XIBGKW-CzHIphTX.js → infoDiagram-27XIBGKW-CV6e39nj.js} +1 -1
  70. package/web/assets/{ishikawaDiagram-5VMMS53U-1aIpAo5f.js → ishikawaDiagram-5VMMS53U-BZ317iz5.js} +1 -1
  71. package/web/assets/{journeyDiagram-3NMN7TZE-DZFs2eed.js → journeyDiagram-3NMN7TZE-aRfKE67B.js} +1 -1
  72. package/web/assets/{kanban-definition-UXKFOSKX-CFPF7vk4.js → kanban-definition-UXKFOSKX-DRAkPvek.js} +1 -1
  73. package/web/assets/{mermaid-parser.core-eET97OnM.js → mermaid-parser.core-BzKTg0uA.js} +3 -3
  74. package/web/assets/{mermaid.core-D-l2iyMD.js → mermaid.core-cF9385Jc.js} +4 -4
  75. package/web/assets/{mindmap-definition-YA3MSWOX-CzO4WiUd.js → mindmap-definition-YA3MSWOX-CLiDn2-4.js} +1 -1
  76. package/web/assets/mount-DdwNnK_L.js +36 -0
  77. package/web/assets/{packet-AYTQ26CC-DiT50-0V.js → packet-AYTQ26CC-C_A2hl_f.js} +1 -1
  78. package/web/assets/{pegDiagram-XKGWAZYB-DG3Ad8mS.js → pegDiagram-XKGWAZYB-DzWlf-iX.js} +1 -1
  79. package/web/assets/{pie-WAS4IAKB-BGJZoyxN.js → pie-WAS4IAKB-C8_wbVGt.js} +1 -1
  80. package/web/assets/{pieDiagram-E7YTZNPT-C_IdKjBF.js → pieDiagram-E7YTZNPT-CqlAZb6C.js} +1 -1
  81. package/web/assets/{quadrantDiagram-AXDQQJYC-CVvfK1li.js → quadrantDiagram-AXDQQJYC-BmL5gqum.js} +1 -1
  82. package/web/assets/{radar-RG4KPBEZ-C14GlZJM.js → radar-RG4KPBEZ-wXteP-ND.js} +1 -1
  83. package/web/assets/{railroad-74A4TZTK-Cg3OXIZS.js → railroad-74A4TZTK-CiAiQ98V.js} +1 -1
  84. package/web/assets/railroad-abnf-HS5TGJTU-7KYSR92O.js +1 -0
  85. package/web/assets/railroad-ebnf-LZEXJU2U-Dt8zrJqM.js +1 -0
  86. package/web/assets/railroad-peg-WCYAUIDC-NioqZSpU.js +1 -0
  87. package/web/assets/{railroadDiagram-O6MQD6OU-_oTlqB8E.js → railroadDiagram-O6MQD6OU-m83Fr2E6.js} +1 -1
  88. package/web/assets/{requirementDiagram-BXWQKSXE-6_cpGq7p.js → requirementDiagram-BXWQKSXE-DdiUlbNn.js} +1 -1
  89. package/web/assets/{sankeyDiagram-P5KCCOFB-CIDdTiIM.js → sankeyDiagram-P5KCCOFB-DgRmQoC3.js} +1 -1
  90. package/web/assets/{sequenceDiagram-WJ2MYXX4-OmglouV4.js → sequenceDiagram-WJ2MYXX4-CLvzUry_.js} +1 -1
  91. package/web/assets/{settingsRoute-Y4aQ907P.js → settingsRoute-CY886TNp.js} +1 -1
  92. package/web/assets/{stateDiagram-D77RDMKH-lmR5zsjq.js → stateDiagram-D77RDMKH-CXEsod-X.js} +1 -1
  93. package/web/assets/stateDiagram-v2-MP3YSRHH-sOazOE8m.js +1 -0
  94. package/web/assets/{swimlanes-42K2YHIH-BRP1y0vF.js → swimlanes-42K2YHIH-BIXrBhfr.js} +1 -1
  95. package/web/assets/swimlanesDiagram-VR7AAH4N-CEQROFH_.js +8 -0
  96. package/web/assets/{timeline-definition-24CTP7MA-74uE748S.js → timeline-definition-24CTP7MA-CHNGDuRJ.js} +1 -1
  97. package/web/assets/{treeView-Q6P3EWNA-C7rSzN0q.js → treeView-Q6P3EWNA-B5PrKFUx.js} +1 -1
  98. package/web/assets/{treemap-WGGIJYW6-B-Eqh7wJ.js → treemap-WGGIJYW6-CyfiLDp3.js} +1 -1
  99. package/web/assets/useStore-BirjskHz.js +16 -0
  100. package/web/assets/{vennDiagram-4TSXK5OY-9HXkWxEw.js → vennDiagram-4TSXK5OY-DsQXSlTg.js} +1 -1
  101. package/web/assets/{wardley-WFR3VGLG-Bvwh4mZv.js → wardley-WFR3VGLG-BzGhnDYD.js} +1 -1
  102. package/web/assets/{wardleyDiagram-VM6X3IG4-D_t1chE2.js → wardleyDiagram-VM6X3IG4-BIxMxWJH.js} +1 -1
  103. package/web/assets/{xychartDiagram-S5SC5T6Z-BwaxhGtz.js → xychartDiagram-S5SC5T6Z-B6m5ol80.js} +1 -1
  104. package/web/index.html +2 -2
  105. package/web/sw.js +1 -1
  106. package/web/assets/AutomationsView-Cw3-fec-.js +0 -51
  107. package/web/assets/ProviderConnect-BVnL5_ML.js +0 -1
  108. package/web/assets/SessionList-qypmhsdJ.js +0 -1
  109. package/web/assets/architecture-7GRP2DOG-DjnOFWJ6.js +0 -1
  110. package/web/assets/channel-B7AEPkF6.js +0 -1
  111. package/web/assets/classDiagram-ZZMXUADV-BEtpnW4V.js +0 -1
  112. package/web/assets/classDiagram-v2-VYDZK3BY-BEtpnW4V.js +0 -1
  113. package/web/assets/eventmodeling-NTZA5JFV-CYiUVM8V.js +0 -1
  114. package/web/assets/flowDiagram-HODETNUW-kSHsw5bd.js +0 -1
  115. package/web/assets/index-CtJDDeFG.css +0 -1
  116. package/web/assets/mount-Cnz2WLxO.js +0 -36
  117. package/web/assets/railroad-abnf-HS5TGJTU-CzpKO8Mq.js +0 -1
  118. package/web/assets/railroad-ebnf-LZEXJU2U-BBhUgg4O.js +0 -1
  119. package/web/assets/railroad-peg-WCYAUIDC-C2WJ96PH.js +0 -1
  120. package/web/assets/stateDiagram-v2-MP3YSRHH-DJ79XS5i.js +0 -1
  121. package/web/assets/swimlanesDiagram-VR7AAH4N-D08eaEQz.js +0 -8
  122. package/web/assets/useStore-TM8ZVr6X.js +0 -16
package/dist/server.js CHANGED
@@ -3,7 +3,7 @@
3
3
  // First: drop agent-session markers the node inherited before anything reads them.
4
4
  import "./node-env.js";
5
5
  import fs from "node:fs";
6
- import { createNodeUpdateChecker, managedInstall, updateRegistryUrl } from "./node-update.js";
6
+ import { createNodeUpdateChecker, isArchiveInstall, managedInstall, updateRegistryUrl } from "./node-update.js";
7
7
  import { createRemoteSessionAdmission, RemoteSessionAdmissionError } from "./session/remote-session-admission.js";
8
8
  import { requiresExistingSession, resolveTargetSession } from "./session/target-session.js";
9
9
  import path from "node:path";
@@ -114,7 +114,7 @@ import { SessionAllowRules } from "./policy/session-allow.js";
114
114
  import { TerminalManager } from "./terminal.js";
115
115
  import { launchCommand } from "./command-launch.js";
116
116
  import { listMultiplexerSessions } from "./multiplexer.js";
117
- import { createWorktree, removeWorktree, gitRepoRoot } from "./worktree.js";
117
+ import { createWorktree, removeWorktree, gitRepoRoot, isManagedWorktreePath, reapedWorktreeBranch } from "./worktree.js";
118
118
  import { HarnessManager } from "./harness/manager.js";
119
119
  import { startEgressProxyIfEnabled, applySessionSandboxEgress, stopSessionEgress } from "./harness/egress.js";
120
120
  import { initSharedDepCache, sharedDepCacheRoot } from "./harness/dep-cache.js";
@@ -240,9 +240,6 @@ try {
240
240
  if (process.env[key] === undefined)
241
241
  process.env[key] = value;
242
242
  }
243
- if (canonicalNodeConfig.agents && process.env.BIVY_CUSTOM_AGENTS === undefined) {
244
- process.env.BIVY_CUSTOM_AGENTS = JSON.stringify(Object.entries(canonicalNodeConfig.agents).map(([id, spec]) => ({ id, ...spec })));
245
- }
246
243
  if (canonicalNodeConfig.automation?.checks && process.env.BIVY_AUTOMATION_CHECKS === undefined) {
247
244
  process.env.BIVY_AUTOMATION_CHECKS = JSON.stringify(canonicalNodeConfig.automation.checks);
248
245
  }
@@ -827,9 +824,10 @@ const sessionRunPolicy = {
827
824
  if (process.env.BIVY_SESSION_MODEL_FALLBACK) {
828
825
  console.log(`[policy] in-session model reroute enabled: ${process.env.BIVY_SESSION_MODEL_FALLBACK}`);
829
826
  }
827
+ const archiveInstalled = isArchiveInstall(repoRoot);
830
828
  const nodeUpdates = createNodeUpdateChecker({
831
829
  current: currentVersion() ?? "",
832
- registryUrl: () => updateRegistryUrl(appDir, process.env.BIVY_UPDATE_REGISTRY_URL),
830
+ registryUrl: () => updateRegistryUrl(appDir, process.env.BIVY_UPDATE_REGISTRY_URL, archiveInstalled),
833
831
  publish: (state) => broadcast(state),
834
832
  });
835
833
  function runtimeSummary(rt) {
@@ -1081,7 +1079,7 @@ const linkedDevices = createLinkedDeviceController({
1081
1079
  syncPairingMetadata();
1082
1080
  relay?.pushRotate(deliveries);
1083
1081
  broadcast({ type: "devices.updated", devices });
1084
- void resealRetiredAutomationTemplates();
1082
+ void checkAutomationTemplates();
1085
1083
  },
1086
1084
  });
1087
1085
  const accessDevices = createAccessDeviceController({
@@ -2123,11 +2121,9 @@ retireTranscriptsDir();
2123
2121
  const sessionNewDedupe = createSessionNewDedupe();
2124
2122
  const dedupeSessionNew = (requestId, create) => sessionNewDedupe.run(requestId, create);
2125
2123
  // Idempotency for `prompt`, keyed by the client's clientMessageId (the same
2126
- // generic key->promise cache as above, reused for a different key) — see
2127
- // issue #154's queued follow-ups. A client that isn't sure whether a queued
2128
- // item's send actually reached the node before the socket dropped (see
2129
- // FollowupCoordinator.retrySending) resends it verbatim after reconnecting;
2130
- // this makes that safe by collapsing a retried clientMessageId onto the
2124
+ // generic key->promise cache as above, reused for a different key). A client
2125
+ // that isn't sure whether a prompt reached the node before the socket dropped
2126
+ // resends it verbatim after reconnecting; this makes that safe by collapsing a retried clientMessageId onto the
2131
2127
  // original broadcast + turn instead of double-prompting the runtime. The key is
2132
2128
  // kept for a day, not session.new's 10 minutes: a phone backgrounded mid-send
2133
2129
  // can reconnect long after the turn ended, and an expired key turned its
@@ -3548,7 +3544,7 @@ function startRelayIfConfigured() {
3548
3544
  if (config.controlPlaneUrl && config.enrollmentToken) {
3549
3545
  sessionAdvertiseTarget = { controlPlaneUrl: config.controlPlaneUrl, enrollmentToken: config.enrollmentToken };
3550
3546
  void syncModelAuthFromControlPlane();
3551
- void resealRetiredAutomationTemplates();
3547
+ void checkAutomationTemplates();
3552
3548
  if (remoteRuntimeEnabled()) {
3553
3549
  // Stage 3: layer a control-plane-backed location registry UNDER the in-memory
3554
3550
  // one (so a lookup surviving a restart resolves from durable state), then
@@ -3678,27 +3674,36 @@ async function modelAuthFetch(pathname, init = {}) {
3678
3674
  }
3679
3675
  // A device revoke rotates the room key. Re-seal this node's stored automation
3680
3676
  // instructions under the new key so the node and its devices can still read
3681
- // them, then drop the retired keys. Retried at startup until it completes.
3682
- async function resealRetiredAutomationTemplates() {
3683
- const retired = pairingStore.retiredRoomKeys();
3684
- if (!retired.length || !sessionAdvertiseTarget)
3677
+ // them, then drop the retired keys. Instructions no key opens are flagged so the
3678
+ // automation list asks for them again. Runs at every start (retried there until
3679
+ // it completes), after a revoke, and when a run can't open its instructions.
3680
+ let automationTemplateCheck = null;
3681
+ function checkAutomationTemplates() {
3682
+ automationTemplateCheck ??= reconcileAutomationTemplates().finally(() => { automationTemplateCheck = null; });
3683
+ return automationTemplateCheck;
3684
+ }
3685
+ async function reconcileAutomationTemplates() {
3686
+ if (!sessionAdvertiseTarget)
3685
3687
  return;
3688
+ const retired = pairingStore.retiredRoomKeys();
3686
3689
  const current = pairingStore.roomKey();
3687
3690
  try {
3688
3691
  const res = await modelAuthFetch("/node/automation-templates");
3689
3692
  if (!res?.ok)
3690
3693
  throw new Error(`control plane returned ${res?.status}`);
3691
3694
  const { templates } = await res.json();
3695
+ const call = async (pathname, init) => {
3696
+ const out = await modelAuthFetch(pathname, init);
3697
+ if (!out?.ok)
3698
+ throw new Error(`control plane returned ${out?.status}`);
3699
+ };
3692
3700
  const result = await resealAutomationTemplates({
3693
3701
  nodeId: identity.nodeId,
3694
3702
  current,
3695
3703
  retired,
3696
3704
  templates,
3697
- save: async (id, templateCiphertext) => {
3698
- const saved = await modelAuthFetch(`/node/automation-templates/${encodeURIComponent(id)}`, { method: "PUT", body: JSON.stringify({ templateCiphertext }) });
3699
- if (!saved?.ok)
3700
- throw new Error(`control plane returned ${saved?.status}`);
3701
- },
3705
+ save: (id, templateCiphertext) => call(`/node/automation-templates/${encodeURIComponent(id)}`, { method: "PUT", body: JSON.stringify({ templateCiphertext }) }),
3706
+ markUnreadable: (id) => call(`/node/automation-templates/${encodeURIComponent(id)}/unreadable`, { method: "POST" }),
3702
3707
  });
3703
3708
  if (result.resealed)
3704
3709
  console.log(`[automation-reseal] re-sealed ${result.resealed} automation template(s) under the rotated room key`);
@@ -3706,8 +3711,9 @@ async function resealRetiredAutomationTemplates() {
3706
3711
  console.warn(`[automation-reseal] ${result.unreadable.length} automation template(s) were sealed under a key this node no longer has: ${result.unreadable.join(", ")}`);
3707
3712
  // Another rotation during this pass retired `current`; run again for it.
3708
3713
  if (!pairingStore.roomKey().equals(current))
3709
- return void resealRetiredAutomationTemplates();
3710
- pairingStore.forgetRetiredRoomKeys();
3714
+ return reconcileAutomationTemplates();
3715
+ if (retired.length)
3716
+ pairingStore.forgetRetiredRoomKeys();
3711
3717
  }
3712
3718
  catch (error) {
3713
3719
  console.warn("[automation-reseal] will retry at next start:", error.message);
@@ -5477,6 +5483,7 @@ async function executeWorkItem(item, report, signal) {
5477
5483
  item = { ...item, body: template.instructions };
5478
5484
  }
5479
5485
  catch {
5486
+ void checkAutomationTemplates();
5480
5487
  throw new Error("could not decrypt automation instructions on this node");
5481
5488
  }
5482
5489
  }
@@ -7004,10 +7011,6 @@ function closeIdleSessions() {
7004
7011
  closeSessionRecord(record, "idle-timeout");
7005
7012
  }
7006
7013
  }
7007
- function isManagedWorktreePath(wtPath) {
7008
- const parts = path.resolve(wtPath).split(path.sep);
7009
- return parts.length >= 3 && parts[parts.length - 3] === ".bivy" && parts[parts.length - 2] === "worktrees";
7010
- }
7011
7014
  function sessionTimestampMs(value) {
7012
7015
  const ms = value ? new Date(value).getTime() : NaN;
7013
7016
  return Number.isFinite(ms) ? ms : 0;
@@ -8447,8 +8450,8 @@ async function tryAttachLiveRemote(sessionId, options) {
8447
8450
  }
8448
8451
  }
8449
8452
  /** Re-open a runtime that still reports streaming shortly after a manual Stop.
8450
- * The client is settled immediately by the abort handler; prompts arriving from
8451
- * its follow-up queue await this promise and therefore cannot be steered into the
8453
+ * The client is settled immediately by the abort handler; prompts arriving
8454
+ * right after the Stop await this promise and therefore cannot be steered into the
8452
8455
  * stale in-process Pi turn. */
8453
8456
  async function recoverRecordAfterAbort(record) {
8454
8457
  const oldSession = record.session;
@@ -8621,20 +8624,21 @@ async function createSession(workspace = defaultWorkspace, sessionFile, opts = {
8621
8624
  // or branch is gone, we leave it to the invariant to fail safe rather than
8622
8625
  // corrupt a neighbour. The clone root is reconstructed from `source`
8623
8626
  // (`repo:owner/repo`) because stored `workspace` is the old worktree path.
8624
- if (requestedSessionFile && !worktree && storedMeta?.worktree && storedMeta?.branch) {
8625
- const parsedSource = parseRepoSource(storedMeta.source);
8627
+ const reapedBranch = requestedSessionFile && !worktree ? reapedWorktreeBranch(storedMeta) : undefined;
8628
+ if (reapedBranch) {
8629
+ const parsedSource = parseRepoSource(storedMeta?.source);
8626
8630
  if (parsedSource) {
8627
8631
  const repoDir = path.join(reposRoot, `${parsedSource.owner}__${parsedSource.repo}`);
8628
8632
  try {
8629
8633
  // Clear any stale registration left by a dir that was rm'd out from under
8630
8634
  // git, so re-adding the branch's worktree doesn't hit "already checked out".
8631
8635
  runGit(["worktree", "prune"], repoDir);
8632
- const reprovisioned = await createWorktree({ repoDir, id: storedMeta.branch, branch: storedMeta.branch });
8636
+ const reprovisioned = await createWorktree({ repoDir, id: reapedBranch, branch: reapedBranch });
8633
8637
  worktree = reprovisioned;
8634
8638
  runtimeWorkspace = reprovisioned.path;
8635
8639
  }
8636
8640
  catch (error) {
8637
- console.warn(`Could not re-provision worktree for resumed session on ${storedMeta.branch}: ${error instanceof Error ? error.message : String(error)}`);
8641
+ console.warn(`Could not re-provision worktree for resumed session on ${reapedBranch}: ${error instanceof Error ? error.message : String(error)}`);
8638
8642
  }
8639
8643
  }
8640
8644
  }
package/dist/worktree.js CHANGED
@@ -19,6 +19,23 @@ export function branchSlug(input) {
19
19
  .replace(/^[-.]+|[-.]+$/g, "")
20
20
  .slice(0, 60) || "task");
21
21
  }
22
+ /** A Bivy-managed worktree lives at `<repo>/.bivy/worktrees/<name>`. */
23
+ export function isManagedWorktreePath(wtPath) {
24
+ const parts = path.resolve(wtPath).split(path.sep);
25
+ return parts.length >= 3 && parts[parts.length - 3] === ".bivy" && parts[parts.length - 2] === "worktrees";
26
+ }
27
+ /**
28
+ * The branch to re-provision when a worktree session is reopened after its
29
+ * directory was removed, else undefined. Worktree cleanup clears `worktree` but
30
+ * leaves `workspace` on the old managed path, so either one marks the session
31
+ * as having run in a worktree.
32
+ */
33
+ export function reapedWorktreeBranch(meta) {
34
+ if (!meta?.branch)
35
+ return undefined;
36
+ const wtPath = meta.worktree ?? (meta.workspace && isManagedWorktreePath(meta.workspace) ? meta.workspace : undefined);
37
+ return wtPath && !fs.existsSync(wtPath) ? meta.branch : undefined;
38
+ }
22
39
  /** The toplevel of the git repo containing `dir`, or undefined if not a repo. */
23
40
  export async function gitRepoRoot(dir) {
24
41
  try {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bivy/bivy",
3
- "version": "0.21.1",
3
+ "version": "0.21.2-staging.10",
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.",