@bivy/bivy 0.20.9-staging.34 → 0.20.9-staging.35

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 (116) hide show
  1. package/README.md +1 -2
  2. package/dist/apps/styles.css +1 -1
  3. package/dist/controllers/credential-commands.js +3 -27
  4. package/dist/credentials/api.js +2 -43
  5. package/dist/credentials/document.js +6 -14
  6. package/dist/credentials/store.js +5 -6
  7. package/dist/server.js +54 -213
  8. package/package.json +2 -2
  9. package/web/assets/AutomationsView-BN4WXo-s.js +51 -0
  10. package/web/assets/ChangesCard-BLxRc1SD.js +4 -0
  11. package/web/assets/LibraryView-BG-p4ZVG.js +2 -0
  12. package/web/assets/{ProviderConnect-DcZq9COd.js → ProviderConnect-D_cUR8Nn.js} +1 -1
  13. package/web/assets/SessionList-uP86Sp-P.js +1 -0
  14. package/web/assets/Settings-QdmsBSqy.js +10 -0
  15. package/web/assets/{Sheet-C_l8Qiz6.js → Sheet-DxeBQ9hh.js} +1 -1
  16. package/web/assets/{StatusDot-dpbztK53.js → Spinner-CPdRfDcT.js} +1 -1
  17. package/web/assets/{Terminal-cgiGAc8k.js → Terminal-BA-H41j5.js} +1 -1
  18. package/web/assets/{VoiceSettings-mTi8Ab7q.js → VoiceSettings-Bzo9CUrH.js} +1 -1
  19. package/web/assets/{abnfDiagram-VCTEODGH-DKCrQ2G4.js → abnfDiagram-VCTEODGH-DSJSYU79.js} +1 -1
  20. package/web/assets/architecture-7GRP2DOG-B9W62Fx_.js +1 -0
  21. package/web/assets/{architectureDiagram-5GKGNRK7-BwWDMLXc.js → architectureDiagram-5GKGNRK7-C5CVECpy.js} +1 -1
  22. package/web/assets/{blockDiagram-I7D4REHJ-9RYpTd76.js → blockDiagram-I7D4REHJ-lfmzpHvp.js} +1 -1
  23. package/web/assets/{c4Diagram-7LVT6UL2-C4WWj2Vs.js → c4Diagram-7LVT6UL2-lcarA-nH.js} +1 -1
  24. package/web/assets/channel-BXl-2oyN.js +1 -0
  25. package/web/assets/{chunk-4HAMMTFA-BoI4F3m1.js → chunk-4HAMMTFA-8WD1Igor.js} +1 -1
  26. package/web/assets/{chunk-75Z2AOVW-CTKoxMoa.js → chunk-75Z2AOVW-Jh9IYxee.js} +1 -1
  27. package/web/assets/{chunk-DU6HZSFF-C2tT7Twc.js → chunk-DU6HZSFF-C-F06-i-.js} +1 -1
  28. package/web/assets/{chunk-F27PBJKO-8i5XnuZF.js → chunk-F27PBJKO-BfheyUJr.js} +1 -1
  29. package/web/assets/{chunk-GMAD6QVW-Co8GsPRG.js → chunk-GMAD6QVW-C4QeycFz.js} +1 -1
  30. package/web/assets/{chunk-GVQU2GXP-BJ6Ojn0H.js → chunk-GVQU2GXP-BZllZVzj.js} +1 -1
  31. package/web/assets/{chunk-IMKFNOWR-e9cqKsBR.js → chunk-IMKFNOWR-CmYszTC0.js} +1 -1
  32. package/web/assets/{chunk-L3NEJ4N5-D9ZfmxbD.js → chunk-L3NEJ4N5-HYod6GBQ.js} +1 -1
  33. package/web/assets/{chunk-OSK3NFVY-0JBTHOE8.js → chunk-OSK3NFVY-BCin4kBb.js} +1 -1
  34. package/web/assets/{chunk-P2QGCYS3-BIg0jTg4.js → chunk-P2QGCYS3-hfC5iZ2m.js} +1 -1
  35. package/web/assets/{chunk-POPQ4Y6H-D6qxbpsr.js → chunk-POPQ4Y6H-DDFroJcO.js} +1 -1
  36. package/web/assets/{chunk-PWAF6VOD-DZW_AvL4.js → chunk-PWAF6VOD-CzGDhfaI.js} +1 -1
  37. package/web/assets/{chunk-SHT3W25Y-C9MSHwa2.js → chunk-SHT3W25Y-C2OsFNUA.js} +1 -1
  38. package/web/assets/{chunk-SVP7TREG-CMPUFvQb.js → chunk-SVP7TREG-DpgjbZ29.js} +1 -1
  39. package/web/assets/{chunk-TICWLB2K-Dc7nqh9g.js → chunk-TICWLB2K-DCfsEWvj.js} +1 -1
  40. package/web/assets/classDiagram-ZZMXUADV-eeGC4O7K.js +1 -0
  41. package/web/assets/classDiagram-v2-VYDZK3BY-eeGC4O7K.js +1 -0
  42. package/web/assets/{cynefin-OW5HDTMX-BT57AAKC.js → cynefin-OW5HDTMX-Dw490MV0.js} +1 -1
  43. package/web/assets/{cynefinDiagram-5FMLGOSQ-n-ipb6S7.js → cynefinDiagram-5FMLGOSQ-MRqdtb3R.js} +1 -1
  44. package/web/assets/{dagre-GXQ25YYZ-BnVzkDsP.js → dagre-GXQ25YYZ-GVsrjWt8.js} +1 -1
  45. package/web/assets/{diagram-S7CK7UJ4-BVioSGH9.js → diagram-S7CK7UJ4-dBynfuwz.js} +1 -1
  46. package/web/assets/{diagram-UQ7AKVKN-BQv4C5rR.js → diagram-UQ7AKVKN-CvzMkQIm.js} +1 -1
  47. package/web/assets/{diagram-VSXAHHWV-C7Wej_5I.js → diagram-VSXAHHWV-DM0Y3A03.js} +1 -1
  48. package/web/assets/{diagram-VX7I27RA-_FGhGEH-.js → diagram-VX7I27RA-B8QuZJJ1.js} +1 -1
  49. package/web/assets/{diagram-Z3DM3KII-DWrf-9yh.js → diagram-Z3DM3KII-DsHpaaVD.js} +1 -1
  50. package/web/assets/{ebnfDiagram-PWID7BFC-6khuXE0E.js → ebnfDiagram-PWID7BFC-D5d-fRlp.js} +1 -1
  51. package/web/assets/{erDiagram-RLTQ6QDP-DfHFa81O.js → erDiagram-RLTQ6QDP-BntqSa-X.js} +1 -1
  52. package/web/assets/eventmodeling-NTZA5JFV-DHY9JONZ.js +1 -0
  53. package/web/assets/flowDiagram-HODETNUW-b1Y1u8zB.js +1 -0
  54. package/web/assets/{ganttDiagram-EL5Y4UJY-Dp9UUO-3.js → ganttDiagram-EL5Y4UJY-BiSe6RPt.js} +1 -1
  55. package/web/assets/{gitGraph-4MIJSDKK-DuinPWHD.js → gitGraph-4MIJSDKK-crNXcNI6.js} +1 -1
  56. package/web/assets/{gitGraphDiagram-WWUBYQGX-B0WCV5Dy.js → gitGraphDiagram-WWUBYQGX-Gq8bbUrV.js} +1 -1
  57. package/web/assets/index-BKSbod42.css +1 -0
  58. package/web/assets/{index-3STLeiP_.js → index-C2l0RrVm.js} +2 -2
  59. package/web/assets/{info-A6RAGUB7-CHLdK2lJ.js → info-A6RAGUB7-D7ku5afp.js} +1 -1
  60. package/web/assets/{infoDiagram-27XIBGKW-DtwI8_Wr.js → infoDiagram-27XIBGKW-DKOdetB1.js} +1 -1
  61. package/web/assets/{ishikawaDiagram-5VMMS53U-DrU0f09X.js → ishikawaDiagram-5VMMS53U-BBXgWpq6.js} +1 -1
  62. package/web/assets/{journeyDiagram-3NMN7TZE-CCf6Dwuf.js → journeyDiagram-3NMN7TZE-C50GQXpS.js} +1 -1
  63. package/web/assets/{kanban-definition-UXKFOSKX-Dq6bxV8s.js → kanban-definition-UXKFOSKX-DX4E75a1.js} +1 -1
  64. package/web/assets/{mermaid-parser.core-BqlBu2gd.js → mermaid-parser.core-ZebqYrQW.js} +3 -3
  65. package/web/assets/{mermaid.core-CGeH3snX.js → mermaid.core-CEOL8xFx.js} +4 -4
  66. package/web/assets/{mindmap-definition-YA3MSWOX-BQypm25v.js → mindmap-definition-YA3MSWOX-DQXDkaGH.js} +1 -1
  67. package/web/assets/mount-shoNM5J3.js +36 -0
  68. package/web/assets/{packet-AYTQ26CC-C3hkXwnH.js → packet-AYTQ26CC-rr9WW6AZ.js} +1 -1
  69. package/web/assets/{pegDiagram-XKGWAZYB-Cuezekh9.js → pegDiagram-XKGWAZYB-DC6KGFpM.js} +1 -1
  70. package/web/assets/{pie-WAS4IAKB-BLbLKNL9.js → pie-WAS4IAKB-Chrwh9jb.js} +1 -1
  71. package/web/assets/{pieDiagram-E7YTZNPT-Dh6pwEKU.js → pieDiagram-E7YTZNPT-BxH2hKmV.js} +1 -1
  72. package/web/assets/{quadrantDiagram-AXDQQJYC-Eif7oTUZ.js → quadrantDiagram-AXDQQJYC-DP_tHDta.js} +1 -1
  73. package/web/assets/{radar-RG4KPBEZ-CkBcZ874.js → radar-RG4KPBEZ-B3m2rpw6.js} +1 -1
  74. package/web/assets/{railroad-74A4TZTK-JtNCOtjW.js → railroad-74A4TZTK-CuMQsitQ.js} +1 -1
  75. package/web/assets/railroad-abnf-HS5TGJTU--xUmAXKs.js +1 -0
  76. package/web/assets/railroad-ebnf-LZEXJU2U-BLsOkOHg.js +1 -0
  77. package/web/assets/railroad-peg-WCYAUIDC-CL2DhE_z.js +1 -0
  78. package/web/assets/{railroadDiagram-O6MQD6OU-CJ0zen7b.js → railroadDiagram-O6MQD6OU-D6n_hu74.js} +1 -1
  79. package/web/assets/{requirementDiagram-BXWQKSXE-D5EmmhBX.js → requirementDiagram-BXWQKSXE-TCkLe8CH.js} +1 -1
  80. package/web/assets/{sankeyDiagram-P5KCCOFB-CpSU9XvZ.js → sankeyDiagram-P5KCCOFB-B2dVDgg8.js} +1 -1
  81. package/web/assets/{sequenceDiagram-WJ2MYXX4-DS9yh-En.js → sequenceDiagram-WJ2MYXX4-B3fjTjZH.js} +1 -1
  82. package/web/assets/{settingsRoute-CXooOZge.js → settingsRoute-C1RuP_I9.js} +1 -1
  83. package/web/assets/{stateDiagram-D77RDMKH-s0vohbtg.js → stateDiagram-D77RDMKH-CpIdCYNP.js} +1 -1
  84. package/web/assets/stateDiagram-v2-MP3YSRHH-DKes1G1a.js +1 -0
  85. package/web/assets/{swimlanes-42K2YHIH-Bk5QsrpR.js → swimlanes-42K2YHIH-BMxrWwFI.js} +1 -1
  86. package/web/assets/swimlanesDiagram-VR7AAH4N-Diu117lT.js +8 -0
  87. package/web/assets/{timeline-definition-24CTP7MA-CFuk44Ce.js → timeline-definition-24CTP7MA-Dm4tF8yN.js} +1 -1
  88. package/web/assets/{treeView-Q6P3EWNA-DmCDmIp1.js → treeView-Q6P3EWNA-l-13N1XB.js} +1 -1
  89. package/web/assets/{treemap-WGGIJYW6-taMzWoTg.js → treemap-WGGIJYW6-BxdHeQGQ.js} +1 -1
  90. package/web/assets/useStore-BR0SJhpC.js +16 -0
  91. package/web/assets/{vennDiagram-4TSXK5OY-BUKABlPl.js → vennDiagram-4TSXK5OY-e0kljBOl.js} +1 -1
  92. package/web/assets/{wardley-WFR3VGLG-BnNRb9_w.js → wardley-WFR3VGLG-BtVNA4a-.js} +1 -1
  93. package/web/assets/{wardleyDiagram-VM6X3IG4-Bk-JQDfa.js → wardleyDiagram-VM6X3IG4-Bb2YkT8Z.js} +1 -1
  94. package/web/assets/{xychartDiagram-S5SC5T6Z-UARcsy88.js → xychartDiagram-S5SC5T6Z-CdeQ4ZTU.js} +1 -1
  95. package/web/index.html +2 -2
  96. package/web/sw.js +1 -1
  97. package/dist/hosted-custody.js +0 -30
  98. package/web/assets/AutomationsView-ByhN0iAC.js +0 -51
  99. package/web/assets/ChangesCard-Dkca25Id.js +0 -4
  100. package/web/assets/LibraryView-D3XxcJiu.js +0 -2
  101. package/web/assets/SessionList-CSl6olZS.js +0 -1
  102. package/web/assets/Settings-CI6PV7Al.js +0 -10
  103. package/web/assets/architecture-7GRP2DOG-CWj-KJ1u.js +0 -1
  104. package/web/assets/channel-CHtVGBTS.js +0 -1
  105. package/web/assets/classDiagram-ZZMXUADV-D2NqYopu.js +0 -1
  106. package/web/assets/classDiagram-v2-VYDZK3BY-D2NqYopu.js +0 -1
  107. package/web/assets/eventmodeling-NTZA5JFV-C6uGitqg.js +0 -1
  108. package/web/assets/flowDiagram-HODETNUW-C_aWcAPC.js +0 -1
  109. package/web/assets/index-DtEJd4mp.css +0 -1
  110. package/web/assets/mount-B1a0Zf1O.js +0 -36
  111. package/web/assets/railroad-abnf-HS5TGJTU-BkYs956v.js +0 -1
  112. package/web/assets/railroad-ebnf-LZEXJU2U-PMvT_JN9.js +0 -1
  113. package/web/assets/railroad-peg-WCYAUIDC-dTeHnWpJ.js +0 -1
  114. package/web/assets/stateDiagram-v2-MP3YSRHH-BRD10_kF.js +0 -1
  115. package/web/assets/swimlanesDiagram-VR7AAH4N-CA1cNOQT.js +0 -8
  116. package/web/assets/useStore-UrxvGCn8.js +0 -16
package/README.md CHANGED
@@ -228,8 +228,7 @@ bivy secrets ref github.repo-token op://Bivy/GitHub/repo-token
228
228
 
229
229
  **Not every CLI login syncs.** Native agent logins may still be per-machine;
230
230
  GitHub App private-key sync is separately opt-in. If you lose every node and
231
- device able to unwrap a vault, you must sign in to providers again. Explicit
232
- hosted-provisioning custody grants are separate from ordinary encrypted sync.
231
+ device able to unwrap a vault, you must sign in to providers again.
233
232
 
234
233
  [Credential sync and runtime coverage →](docs/credential-sync.md) ·
235
234
  [Credentials guide →](docs/credentials-guide.md) ·
@@ -1517,7 +1517,6 @@ button.sheet-action:last-child { border-bottom: 0; }
1517
1517
  .vault-detail-grid { display: grid; grid-template-columns: minmax(110px, auto) 1fr; gap: 10px 18px; padding: 14px; border: 1px solid var(--line); border-radius: var(--radius-md); background: var(--surface-2); }
1518
1518
  .vault-detail-grid strong { min-width: 0; overflow-wrap: anywhere; }
1519
1519
  .vault-detail-grid code { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
1520
- .vault-custody-note { padding: 12px 14px; border-left: 3px solid var(--accent); border-radius: var(--radius-sm) var(--radius-md) var(--radius-md) var(--radius-sm); background: var(--surface-2); line-height: 1.5; }
1521
1520
  .vault-advanced { padding: 10px 0; border-top: 1px solid var(--line); }
1522
1521
  .vault-advanced summary { cursor: pointer; color: var(--ink); font-weight: 600; }
1523
1522
  .vault-advanced[open] summary { margin-bottom: 10px; }
@@ -1916,6 +1915,7 @@ button.template-card.is-featured .template-card-icon {
1916
1915
  .autom-runner-select-copy span { color: var(--muted); font-size: 12px; line-height: 1.35; }
1917
1916
  .autom-runner-select-row .autom-inline-select { flex: none; width: min(42%, 190px); }
1918
1917
  .autom-runner-help {
1918
+ display: flex; align-items: center; flex-wrap: wrap; gap: var(--space-2);
1919
1919
  padding: 10px 12px; border-top: 1px solid var(--line);
1920
1920
  color: var(--ink); background: color-mix(in srgb, var(--warn) 9%, var(--surface));
1921
1921
  font-size: 12px; line-height: 1.45;
@@ -10,7 +10,7 @@
10
10
  // provider-list helpers) as composition deps.
11
11
  import { testProviderCredential } from "../runtime/credentials.js";
12
12
  import { NativeAuthPreviewService } from "../runtime/native-auth-preview.js";
13
- import { exportAccountApiKeys, exportAccountOAuthCredentials, importAccountOAuthCredentials, exportRecordTombstones, listCredentialRecords, removeProviderCredential, setProviderApiKeyLabeled, setProviderReferenceLabeled, setCredentialSync, setCredentialUnattended, getCredentialPresets, setActiveCredentialPreset, setCredentialPresetMapping, } from "../credentials/api.js";
13
+ import { exportAccountApiKeys, exportAccountOAuthCredentials, importAccountOAuthCredentials, exportRecordTombstones, listCredentialRecords, removeProviderCredential, setProviderApiKeyLabeled, setProviderReferenceLabeled, setCredentialSync, getCredentialPresets, setActiveCredentialPreset, setCredentialPresetMapping, } from "../credentials/api.js";
14
14
  export function createCredentialCommands(deps) {
15
15
  const { credsDir } = deps;
16
16
  const nativeAuth = new NativeAuthPreviewService(credsDir);
@@ -95,8 +95,7 @@ export function createCredentialCommands(deps) {
95
95
  async "credential.remove"(msg, ctx) {
96
96
  try {
97
97
  await removeProviderCredential(credsDir, String(msg.provider ?? ""), String(msg.label ?? ""));
98
- // Deleting a granted credential must also remove its escrowed cloud copy.
99
- await deps.pushModelAuthToControlPlane({ allowEmptyHostedEscrow: true });
98
+ await deps.pushModelAuthToControlPlane();
100
99
  await deps.refreshSessionAfterAuth();
101
100
  deps.sendEvent({ type: "credentials.records", records: await listCredentialRecords(credsDir) });
102
101
  deps.broadcast({ type: "providers.list", providers: await deps.listProvidersUnified() });
@@ -112,8 +111,7 @@ export function createCredentialCommands(deps) {
112
111
  try {
113
112
  const sync = msg.sync === "node" ? "node" : "account";
114
113
  await setCredentialSync(credsDir, String(msg.provider ?? ""), String(msg.label ?? ""), sync);
115
- // Demoting to machine-only revokes any custody grant; let it clear the escrow.
116
- await deps.pushModelAuthToControlPlane({ allowEmptyHostedEscrow: sync === "node" });
114
+ await deps.pushModelAuthToControlPlane();
117
115
  deps.sendEvent({ type: "credentials.records", records: await listCredentialRecords(credsDir) });
118
116
  ctx.reply({ type: "credential.sync.set.ok", requestId: msg.requestId });
119
117
  }
@@ -123,28 +121,6 @@ export function createCredentialCommands(deps) {
123
121
  ctx.reply({ type: "credential.sync.set.error", requestId: msg.requestId, error: message });
124
122
  }
125
123
  },
126
- async "credential.unattended.set"(msg, ctx) {
127
- const provider = String(msg.provider ?? "");
128
- const label = String(msg.label ?? "");
129
- const previous = (await listCredentialRecords(credsDir)).find((record) => record.provider === provider.trim().toLowerCase() && record.label === label)?.unattended === true;
130
- try {
131
- await setCredentialUnattended(credsDir, provider, label, msg.unattended === true);
132
- // Fail loud: the grant reply must reflect whether the encrypted cloud
133
- // copy actually reached the control plane. An explicit disable is the
134
- // one caller allowed to publish an empty escrow snapshot.
135
- await deps.pushModelAuthToControlPlane({ throwOnFailure: true, allowEmptyHostedEscrow: msg.unattended !== true });
136
- deps.sendEvent({ type: "credentials.records", records: await listCredentialRecords(credsDir) });
137
- ctx.reply({ type: "credential.unattended.set.ok", requestId: msg.requestId });
138
- }
139
- catch (error) {
140
- // The UI must never claim an encrypted Cloud copy exists when custody
141
- // publication failed. Restore the prior local grant before rejecting.
142
- await setCredentialUnattended(credsDir, provider, label, previous).catch(() => { });
143
- deps.sendEvent({ type: "credentials.records", records: await listCredentialRecords(credsDir) });
144
- const message = error instanceof Error ? error.message : String(error);
145
- ctx.reply({ type: "credential.unattended.set.error", requestId: msg.requestId, error: message });
146
- }
147
- },
148
124
  // "Test connection": a bounded, non-secret liveness probe for one credential
149
125
  // record (see credentials/api.ts testCredential). The reply carries only
150
126
  // ok/at/reason — the credential's own token never leaves this handler.
@@ -132,18 +132,6 @@ export async function importCredentialRecords(credsDir, records, deletedAt = {})
132
132
  }
133
133
  return createCredentialVault(credsDir).importRecords(list, tombstones);
134
134
  }
135
- /** Apply a hosted custody snapshot as an authoritative filtered set. */
136
- export async function reconcileHostedCredentialRecords(credsDir, records, previous) {
137
- const current = Object.values(records ?? {}).filter((record) => Boolean(record) && typeof record === "object" && typeof record.provider === "string" && typeof record.label === "string");
138
- const currentIds = new Set(current.map((record) => `${record.provider}\u0000${record.label}`));
139
- const store = createCredentialVault(credsDir);
140
- for (const prior of previous) {
141
- if (!currentIds.has(`${prior.provider}\u0000${prior.label}`))
142
- await store.deleteRecord(prior.provider, prior.label);
143
- }
144
- await importCredentialRecords(credsDir, records);
145
- return current.map((record) => ({ provider: record.provider, label: record.label }));
146
- }
147
135
  /** Export provider revocations for cross-node convergence. */
148
136
  export async function exportProviderAuthTombstones(credsDir) {
149
137
  return createCredentialVault(credsDir).exportTombstones();
@@ -225,7 +213,7 @@ export async function listCredentialRecords(credsDir) {
225
213
  const verification = await vault.readVerification(record.provider, record.label).catch(() => undefined);
226
214
  const verified = verification ? { lastVerifiedAt: verification.at, lastVerifiedOk: verification.ok } : {};
227
215
  if (source.kind === "reference") {
228
- return { provider: record.provider, label: record.label, kind: "reference", sync: record.sync, origin: record.origin, unattended: record.unattended === true, ref: source.ref, testable: false, ...verified };
216
+ return { provider: record.provider, label: record.label, kind: "reference", sync: record.sync, origin: record.origin, ref: source.ref, testable: false, ...verified };
229
217
  }
230
218
  const cred = source.cred;
231
219
  const summary = {
@@ -234,7 +222,6 @@ export async function listCredentialRecords(credsDir) {
234
222
  kind: cred.type,
235
223
  sync: record.sync,
236
224
  origin: record.origin,
237
- unattended: record.unattended === true,
238
225
  testable: isTestableProvider(record.provider),
239
226
  ...verified,
240
227
  };
@@ -253,7 +240,6 @@ async function labeledMeta(credsDir, provider, label) {
253
240
  return {
254
241
  sync: existing?.sync ?? defaultSyncFor("bivy"),
255
242
  origin: existing?.origin ?? "bivy",
256
- ...(existing?.unattended === true ? { unattended: true } : {}),
257
243
  };
258
244
  }
259
245
  /** Store an API key under a specific label (multi-account). */
@@ -404,33 +390,6 @@ export function getCredentialIngestPolicy(credsDir) {
404
390
  export function setCredentialIngestPolicy(credsDir, policy) {
405
391
  setIngestPolicyFile(defaultPresetsPath(credsDir), policy);
406
392
  }
407
- /** Explicitly grant/revoke use by separately escrowed unattended runners. */
408
- export async function setCredentialUnattended(credsDir, provider, label, unattended) {
409
- const id = provider.trim().toLowerCase();
410
- if (!id)
411
- throw new Error("Provider is required");
412
- const store = createCredentialVault(credsDir);
413
- const existing = await store.readRecord(id, label);
414
- if (!existing)
415
- throw new Error(`No credential for ${id}:${normalizeLabel(label)}`);
416
- if (existing.sync !== "account" && unattended)
417
- throw new Error("A machine-only credential cannot be granted to unattended runners");
418
- if (existing.source.kind === "reference" && unattended)
419
- throw new Error("Password-manager references cannot be resolved by unattended runners");
420
- await store.putRecord({ ...existing, unattended });
421
- }
422
- /** Credentials included in the explicit hosted custody snapshot. */
423
- export async function exportUnattendedRecords(credsDir) {
424
- const records = await createCredentialVault(credsDir).exportSyncableRecords();
425
- return Object.fromEntries(Object.entries(records).filter(([, record]) => record.unattended === true && record.source.kind === "stored"));
426
- }
427
- /** Monotonic logical revision for the filtered custody projection. */
428
- export async function unattendedCredentialRevision(credsDir) {
429
- const store = createCredentialVault(credsDir);
430
- const records = await store.listRecords();
431
- const deletedAt = await store.exportRecordTombstones();
432
- return Math.max(0, ...records.map((record) => Number(record.updatedAt) || 0), ...Object.values(deletedAt).map((stamp) => Number(stamp) || 0));
433
- }
434
393
  /** Set a labeled credential's sync tier — the per-credential opt-out toggle. */
435
394
  export async function setCredentialSync(credsDir, provider, label, sync) {
436
395
  const id = provider.trim().toLowerCase();
@@ -445,5 +404,5 @@ export async function setCredentialSync(credsDir, provider, label, sync) {
445
404
  if (existing.source.kind === "reference" && existing.source.backend === "command" && sync === "account") {
446
405
  throw new Error("A cmd:// reference is node-local (it runs a command on this machine) and cannot be synced.");
447
406
  }
448
- await store.putRecord({ ...existing, sync, ...(sync === "node" ? { unattended: false } : {}) });
407
+ await store.putRecord({ ...existing, sync });
449
408
  }
@@ -137,12 +137,11 @@ export function preferIncomingRecord(local, incoming) {
137
137
  const incomingOauth = oauthOf(incoming);
138
138
  if (!localOauth || !incomingOauth)
139
139
  return true;
140
- // Identical token content means this is a metadata-only change (an
141
- // unattended-runs grant/revoke, a sync-tier move). Token freshness cannot
142
- // order those, so the newer record write wins — without this, a grant made
143
- // on one machine never reaches its peers, and a peer's next hosted-escrow
144
- // push would read as "nothing granted". Equal stamps fall back to canonical
145
- // record content so both merge directions still converge.
140
+ // Identical token content means this is a metadata-only change (a sync-tier
141
+ // move). Token freshness cannot order those, so the newer record write wins —
142
+ // without this, a change made on one machine never reaches its peers. Equal
143
+ // stamps fall back to canonical record content so both merge directions still
144
+ // converge.
146
145
  if (canonicalCredential(localOauth) === canonicalCredential(incomingOauth)) {
147
146
  const localAt = Number(local.updatedAt) || 0;
148
147
  const incomingAt = Number(incoming.updatedAt) || 0;
@@ -269,14 +268,7 @@ export function mergeDocuments(local, incomingCredentials, incomingDeletedAt = {
269
268
  continue;
270
269
  if (!(key in credentials))
271
270
  imported += 1;
272
- // An incoming record with no opinion on unattended custody (undefined — the
273
- // v2 wire and plaintext ingest never carry the flag) must not strip an
274
- // explicit local grant/revoke: agent-side token refreshes flow through this
275
- // merge, and dropping the flag would make the node's next escrow push
276
- // silently revoke the encrypted cloud copy the user still relies on.
277
- credentials[key] = localRecord && incoming.unattended === undefined && localRecord.unattended !== undefined
278
- ? { ...incoming, unattended: localRecord.unattended }
279
- : incoming;
271
+ credentials[key] = incoming;
280
272
  const incomingUpdatedAt = Number(incoming.updatedAt);
281
273
  if (!Number.isFinite(deletedAt[key]) || incomingUpdatedAt > deletedAt[key])
282
274
  delete deletedAt[key];
@@ -555,11 +555,11 @@ export class BivyCredentialStore {
555
555
  // tombstone-newer-wins — see document.ts).
556
556
  const incoming = migrateToV3({ v: 2, providers: snapshot, deletedAt });
557
557
  // The v2 wire carries no record metadata, so migrateToV3 synthesizes
558
- // defaults (sync: "account", origin: "bivy", no unattended flag). Those
559
- // defaults must not clobber local intent — a machine-only sync tier or an
560
- // unattended-runs custody grant — when only the token content is newer
561
- // (e.g. an agent's own TUI refreshed the OAuth token set). Inherit the
562
- // existing record's metadata; the merge engine still decides freshness.
558
+ // defaults (sync: "account", origin: "bivy"). Those defaults must not
559
+ // clobber local intent — a machine-only sync tier — when only the token
560
+ // content is newer (e.g. an agent's own TUI refreshed the OAuth token
561
+ // set). Inherit the existing record's metadata; the merge engine still
562
+ // decides freshness.
563
563
  for (const [key, record] of Object.entries(incoming.credentials)) {
564
564
  const existing = document.credentials[key];
565
565
  if (!existing)
@@ -568,7 +568,6 @@ export class BivyCredentialStore {
568
568
  ...record,
569
569
  sync: existing.sync,
570
570
  origin: existing.origin,
571
- ...(existing.unattended !== undefined ? { unattended: existing.unattended } : {}),
572
571
  };
573
572
  }
574
573
  const result = mergeDocuments(document, incoming.credentials, incoming.deletedAt);