chainlesschain 0.165.4 → 0.165.6

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 (214) hide show
  1. package/package.json +1 -1
  2. package/src/assets/web-panel/.build-hash +1 -1
  3. package/src/assets/web-panel/assets/{AIOps-C__nnraW.js → AIOps-vfoDb6HZ.js} +1 -1
  4. package/src/assets/web-panel/assets/{ActionButton-CsKWHEZ-.js → ActionButton-Cm5f7p93.js} +1 -1
  5. package/src/assets/web-panel/assets/{Analytics-BhIjfjWO.js → Analytics-yRuvgWxa.js} +3 -3
  6. package/src/assets/web-panel/assets/{AppLayout-BjuXFzWZ.js → AppLayout-BROsC4h1.js} +4 -4
  7. package/src/assets/web-panel/assets/{Artifacts-k6whDkZJ.js → Artifacts-CimHApb1.js} +1 -1
  8. package/src/assets/web-panel/assets/{Audit-C4FBMYRh.js → Audit-DI8kr8zI.js} +1 -1
  9. package/src/assets/web-panel/assets/{BackgroundAgents-C_vRrSXk.js → BackgroundAgents-DSLzKy-n.js} +1 -1
  10. package/src/assets/web-panel/assets/{Backup-CngFZeVp.js → Backup-DkH_n2r1.js} +1 -1
  11. package/src/assets/web-panel/assets/{BaseInput-B_H9EBRm.js → BaseInput-CKSrUoRA.js} +1 -1
  12. package/src/assets/web-panel/assets/{Chat-DLEC5kt7.js → Chat-ClwfMXUS.js} +3 -3
  13. package/src/assets/web-panel/assets/ChatBubbleRenderer-CjMaqfL7.js +1 -0
  14. package/src/assets/web-panel/assets/{Checkbox-m3ClR4xh.js → Checkbox-BYTehcQG.js} +1 -1
  15. package/src/assets/web-panel/assets/{Codegen-CmLSFrgp.js → Codegen-B8AMcp6J.js} +1 -1
  16. package/src/assets/web-panel/assets/{Col-DFGQteHN.js → Col-BNSogPo3.js} +1 -1
  17. package/src/assets/web-panel/assets/{Community-EUUVORHE.js → Community-JxPvGb8O.js} +1 -1
  18. package/src/assets/web-panel/assets/{Compact-2PSg0Yr7.js → Compact-DF7bFAgE.js} +1 -1
  19. package/src/assets/web-panel/assets/{Compliance-CcKur7gB.js → Compliance-Bvhqnwrq.js} +1 -1
  20. package/src/assets/web-panel/assets/{Cowork-DK_Fezfa.js → Cowork-BDS0CJvM.js} +3 -3
  21. package/src/assets/web-panel/assets/{Cron-Dbw89xl7.js → Cron-DqvRfxoQ.js} +2 -2
  22. package/src/assets/web-panel/assets/{Crosschain-CH66h_Ec.js → Crosschain-CDcIvnqS.js} +1 -1
  23. package/src/assets/web-panel/assets/{DID-D1igRb17.js → DID-D7jaUxU3.js} +2 -2
  24. package/src/assets/web-panel/assets/{Dashboard-DVAsrobk.js → Dashboard-B5A18wEV.js} +2 -2
  25. package/src/assets/web-panel/assets/{Dropdown-Dl7W8AQs.js → Dropdown-_6IVkGag.js} +1 -1
  26. package/src/assets/web-panel/assets/{EmailListRenderer-D5tpaEQC.js → EmailListRenderer-C9PeiKCG.js} +1 -1
  27. package/src/assets/web-panel/assets/{FamilyGuardDashboard-O_VQf8gq.js → FamilyGuardDashboard-DXjuEHrX.js} +1 -1
  28. package/src/assets/web-panel/assets/{Federation-BTVcgS9n.js → Federation-B4IAR9zv.js} +1 -1
  29. package/src/assets/web-panel/assets/{FormItemContext-BRnkJAIz.js → FormItemContext-CxQeSNx1.js} +1 -1
  30. package/src/assets/web-panel/assets/{GenericCardRenderer-8OWSCkDj.js → GenericCardRenderer-B8GS-Y3N.js} +1 -1
  31. package/src/assets/web-panel/assets/{Git-WXLYTQj2.js → Git-BS8SouUX.js} +2 -2
  32. package/src/assets/web-panel/assets/{Governance-BezaUhtc.js → Governance-Dq9aYb5Q.js} +1 -1
  33. package/src/assets/web-panel/assets/{Inference-CuRay3Gu.js → Inference-BrCEe9AJ.js} +1 -1
  34. package/src/assets/web-panel/assets/{KnowledgeGraph-DfWLDW1m.js → KnowledgeGraph-DybOpiY2.js} +1 -1
  35. package/src/assets/web-panel/assets/{Logs-C55y2pPY.js → Logs-DBpzbRfx.js} +2 -2
  36. package/src/assets/web-panel/assets/MarkdownRenderer-CFyt8iUd.js +1 -0
  37. package/src/assets/web-panel/assets/{Marketplace-Dk08Uycg.js → Marketplace-Cc7CCSCu.js} +1 -1
  38. package/src/assets/web-panel/assets/{McpTools-C11fSB5V.js → McpTools-DFaIhOfu.js} +3 -3
  39. package/src/assets/web-panel/assets/{Memory-mr0eZc97.js → Memory-KCqlp9ol.js} +2 -2
  40. package/src/assets/web-panel/assets/{MobileBridge-C0iCSRbb.js → MobileBridge-Bys84ySN.js} +1 -1
  41. package/src/assets/web-panel/assets/{MobileProjects-CY-xMG8Z.js → MobileProjects-B-6qU-je.js} +1 -1
  42. package/src/assets/web-panel/assets/{Mtc-Bplqc_Wc.js → Mtc-BgY-fvCq.js} +4 -4
  43. package/src/assets/web-panel/assets/{MtcAudit-BlzwsoSM.js → MtcAudit-B8pCmqQJ.js} +4 -4
  44. package/src/assets/web-panel/assets/{Multisig-BBLmTty6.js → Multisig-BbOmYutA.js} +3 -3
  45. package/src/assets/web-panel/assets/{NLProgramming-DtT1uf1W.js → NLProgramming-BocLQz_M.js} +1 -1
  46. package/src/assets/web-panel/assets/{Notes-DcqQAxTK.js → Notes-CSwbxEop.js} +3 -3
  47. package/src/assets/web-panel/assets/{NotificationSettings-cb0kWoZe.js → NotificationSettings-CqFNSYG3.js} +1 -1
  48. package/src/assets/web-panel/assets/{OrderTableRenderer-kw9HZQNN.js → OrderTableRenderer-C6gMfAk7.js} +1 -1
  49. package/src/assets/web-panel/assets/{Organization-DlTlbkUN.js → Organization-QrDXVdMJ.js} +4 -4
  50. package/src/assets/web-panel/assets/{Overflow-pp1qs-oY.js → Overflow-Cxntotjt.js} +1 -1
  51. package/src/assets/web-panel/assets/{P2P-BESLnyYz.js → P2P-CIdBOwNq.js} +2 -2
  52. package/src/assets/web-panel/assets/{PdhVaultBrowser-B-J1mGuf.js → PdhVaultBrowser-CT5aP8rD.js} +3 -3
  53. package/src/assets/web-panel/assets/{Permissions-B36PeT1_.js → Permissions-DH7OM8XU.js} +4 -4
  54. package/src/assets/web-panel/assets/{PersonalDataHub-CTK2ad-3.js → PersonalDataHub-CnJqbWce.js} +3 -3
  55. package/src/assets/web-panel/assets/{Pipeline-zJNGXsac.js → Pipeline-5C3HG1kH.js} +1 -1
  56. package/src/assets/web-panel/assets/{Privacy-b9fz0Qf0.js → Privacy-B4yIp7xK.js} +1 -1
  57. package/src/assets/web-panel/assets/{ProjectInit-DjZbZgiB.js → ProjectInit-BMvt-2M0.js} +2 -2
  58. package/src/assets/web-panel/assets/{ProjectSettings-C-hHJXlD.js → ProjectSettings-CvSTEMRG.js} +2 -2
  59. package/src/assets/web-panel/assets/Projects-BKBJxEek.js +1 -0
  60. package/src/assets/web-panel/assets/Providers-BdwUqFWN.js +1 -0
  61. package/src/assets/web-panel/assets/{QrScannerModal-A0yoQ3-3.js → QrScannerModal-DtnCKlTm.js} +1 -1
  62. package/src/assets/web-panel/assets/{QuickAsk-DrFgFJNC.js → QuickAsk-sTbgwyNr.js} +1 -1
  63. package/src/assets/web-panel/assets/{Recommend-KC8hF1g4.js → Recommend-Bzr14gSb.js} +1 -1
  64. package/src/assets/web-panel/assets/{RemoteSession-DGTPIk6u.js → RemoteSession-C_TF5IaJ.js} +2 -2
  65. package/src/assets/web-panel/assets/{Reputation-QmbwM7Yk.js → Reputation-GCAM4H2K.js} +1 -1
  66. package/src/assets/web-panel/assets/{Row-BxjJylia.js → Row-Dir0r6OQ.js} +1 -1
  67. package/src/assets/web-panel/assets/{RssFeed-B7Wda1J6.js → RssFeed-CpGnlecX.js} +2 -2
  68. package/src/assets/web-panel/assets/{Search-_plQ_YSA.js → Search-DMMamC6u.js} +1 -1
  69. package/src/assets/web-panel/assets/{Security-CD2YJZfx.js → Security-yqay5yG-.js} +4 -4
  70. package/src/assets/web-panel/assets/{Services-qQE7NmwE.js → Services-C5i2wh9G.js} +2 -2
  71. package/src/assets/web-panel/assets/{Skeleton-DgQqkFf7.js → Skeleton-vUqk35yj.js} +1 -1
  72. package/src/assets/web-panel/assets/{Skills-C4C0oFFt.js → Skills-DzcSkhot.js} +1 -1
  73. package/src/assets/web-panel/assets/{Sla-Px2N2B5c.js → Sla-CB6qbYNr.js} +1 -1
  74. package/src/assets/web-panel/assets/{SpeechSettings-DN6C3lQd.js → SpeechSettings-BHUI7cox.js} +1 -1
  75. package/src/assets/web-panel/assets/{SyncSettings-jm5aH_m7.js → SyncSettings-D-RrsxTJ.js} +2 -2
  76. package/src/assets/web-panel/assets/{Tasks-DlkJcv67.js → Tasks-DFxdOJiq.js} +1 -1
  77. package/src/assets/web-panel/assets/{Templates-DyrTHUBs.js → Templates-SKAob_Aq.js} +1 -1
  78. package/src/assets/web-panel/assets/{Tenant-mC434PtL.js → Tenant-BPfsfHQ-.js} +1 -1
  79. package/src/assets/web-panel/assets/{Terminal-o0_s3NgK.js → Terminal-8iw7FpUf.js} +2 -2
  80. package/src/assets/web-panel/assets/{TimelineRenderer-DAGlAXOw.js → TimelineRenderer-C4Gjj5Su.js} +1 -1
  81. package/src/assets/web-panel/assets/{Tokens-BnulS_bs.js → Tokens-COYhN18e.js} +1 -1
  82. package/src/assets/web-panel/assets/{Trigger-suQbVXYM.js → Trigger-B-JzXcf6.js} +1 -1
  83. package/src/assets/web-panel/assets/{Trust-DbIJAPo5.js → Trust-BJ5bg0nI.js} +1 -1
  84. package/src/assets/web-panel/assets/{UkeySign-CQNpbIHi.js → UkeySign-BG_ByRMf.js} +1 -1
  85. package/src/assets/web-panel/assets/{VideoEditing-D8aWCERk.js → VideoEditing-B3kOnSiU.js} +1 -1
  86. package/src/assets/web-panel/assets/{Wallet-CuLpYfZc.js → Wallet-CcoGFnK7.js} +2 -2
  87. package/src/assets/web-panel/assets/{WebAuthn-CsfG8ZMc.js → WebAuthn-DdT8vRC_.js} +4 -4
  88. package/src/assets/web-panel/assets/{WorkflowEditor-BYsEAZbd.js → WorkflowEditor-CZu3M9N8.js} +1 -1
  89. package/src/assets/web-panel/assets/{chat-FT4Um3-I.js → chat-DxJt3Qba.js} +1 -1
  90. package/src/assets/web-panel/assets/{colors-CKbS_Wlt.js → colors-DdYfwJaB.js} +1 -1
  91. package/src/assets/web-panel/assets/{compact-item-CFJtzuJM.js → compact-item-BG3SUzwj.js} +1 -1
  92. package/src/assets/web-panel/assets/{createContext-B9kSRW6K.js → createContext-By51z2-1.js} +1 -1
  93. package/src/assets/web-panel/assets/devWarning-DorRZcI9.js +1 -0
  94. package/src/assets/web-panel/assets/{hasIn-DR-x0Xpi.js → hasIn-B0apZ3KJ.js} +1 -1
  95. package/src/assets/web-panel/assets/{index-DjEP7WkH.js → index-3jWkIu5j.js} +1 -1
  96. package/src/assets/web-panel/assets/{index-7jHr_A_k.js → index-B3rWuPMW.js} +1 -1
  97. package/src/assets/web-panel/assets/{index-DcfxhKXR.js → index-B5rLiEo0.js} +1 -1
  98. package/src/assets/web-panel/assets/{index-tqSEHPRT.js → index-BBPf3TpM.js} +1 -1
  99. package/src/assets/web-panel/assets/index-BETiL0a3.js +1 -0
  100. package/src/assets/web-panel/assets/{index-Cr2FLUsD.js → index-BIrJfqO4.js} +1 -1
  101. package/src/assets/web-panel/assets/{index-Bt4fS2eL.js → index-BLIbGOLX.js} +1 -1
  102. package/src/assets/web-panel/assets/{index-BwR7yc2h.js → index-BP94CrBV.js} +1 -1
  103. package/src/assets/web-panel/assets/{index-BVPU43b1.js → index-BWF8_mkx.js} +1 -1
  104. package/src/assets/web-panel/assets/{index-DShSkww-.js → index-BYINoRLK.js} +1 -1
  105. package/src/assets/web-panel/assets/{index-Bzz0C1BZ.js → index-BZ_0qm4g.js} +1 -1
  106. package/src/assets/web-panel/assets/{index-KqDaCApw.js → index-BewKYrX9.js} +1 -1
  107. package/src/assets/web-panel/assets/{index-BW-t9n-q.js → index-BfWYsB9t.js} +1 -1
  108. package/src/assets/web-panel/assets/index-BhAVHuW8.js +1 -0
  109. package/src/assets/web-panel/assets/{index-B7osrXqy.js → index-Bissefox.js} +3 -3
  110. package/src/assets/web-panel/assets/{index-DW4BWjTk.js → index-BkvcEo4p.js} +1 -1
  111. package/src/assets/web-panel/assets/{index-DdkIhfi-.js → index-Bos5_aMV.js} +1 -1
  112. package/src/assets/web-panel/assets/{index-DTxNy0ZU.js → index-C1C9i79A.js} +1 -1
  113. package/src/assets/web-panel/assets/{index-QBSeNVO3.js → index-CAhHirVH.js} +1 -1
  114. package/src/assets/web-panel/assets/{index-D4LfPvWA.js → index-CNIxXMmN.js} +1 -1
  115. package/src/assets/web-panel/assets/{index-C_enk9I_.js → index-CW93tZDv.js} +1 -1
  116. package/src/assets/web-panel/assets/{index-WE08gMJP.js → index-CWEuZWCK.js} +1 -1
  117. package/src/assets/web-panel/assets/{index-r2VI1Ikk.js → index-Cp6e-hGw.js} +1 -1
  118. package/src/assets/web-panel/assets/{index-Dap9c3eD.js → index-CpjZhgNd.js} +1 -1
  119. package/src/assets/web-panel/assets/{index-DWdqx7zI.js → index-CzmQElZy.js} +1 -1
  120. package/src/assets/web-panel/assets/{index-BGbPnC5x.js → index-DCn8IcxV.js} +1 -1
  121. package/src/assets/web-panel/assets/{index-DiNUEpSQ.js → index-DE76D-kA.js} +1 -1
  122. package/src/assets/web-panel/assets/{index-DtkFqPVA.js → index-DaIz0NAW.js} +1 -1
  123. package/src/assets/web-panel/assets/{index-CGaXUsIs.js → index-Da_WBdLX.js} +1 -1
  124. package/src/assets/web-panel/assets/{index-B3ZX81mK.js → index-Dz49VOyQ.js} +1 -1
  125. package/src/assets/web-panel/assets/{index-BK3TiKVq.js → index-Q2SCX-MI.js} +1 -1
  126. package/src/assets/web-panel/assets/{index-CJmIQ9XR.js → index-RD6O-0C6.js} +1 -1
  127. package/src/assets/web-panel/assets/{index-CvrCCqJt.js → index-SQqOlR3d.js} +1 -1
  128. package/src/assets/web-panel/assets/{index-BI5hP_qy.js → index-ZFbps9po.js} +1 -1
  129. package/src/assets/web-panel/assets/{index-DeSgw-WV.js → index-ZxtP8jG7.js} +1 -1
  130. package/src/assets/web-panel/assets/{index-DMGmg497.js → index-bP_VlDb4.js} +1 -1
  131. package/src/assets/web-panel/assets/{index-BfFCQHL3.js → index-kf57LJ93.js} +1 -1
  132. package/src/assets/web-panel/assets/{index-OSzjTzGM.js → index-oxRrmFO9.js} +1 -1
  133. package/src/assets/web-panel/assets/{index-C2JvmYRc.js → index-p1_8SbI1.js} +1 -1
  134. package/src/assets/web-panel/assets/{initDefaultProps-Dd7n7VgI.js → initDefaultProps-BWJktcg8.js} +1 -1
  135. package/src/assets/web-panel/assets/{motion-BwxouRw1.js → motion-BEXBzUFV.js} +1 -1
  136. package/src/assets/web-panel/assets/{move-DObN0PG9.js → move-BUKwn2sx.js} +1 -1
  137. package/src/assets/web-panel/assets/{mtc-parser-B3nOUQc7.js → mtc-parser-FTI7btqQ.js} +1 -1
  138. package/src/assets/web-panel/assets/{omit-o9WU2Nbl.js → omit-CsLtSPH3.js} +1 -1
  139. package/src/assets/web-panel/assets/parsers-YNLm7sQ0.js +4 -0
  140. package/src/assets/web-panel/assets/{pickAttrs-CL_vvfQ_.js → pickAttrs-DWZxsT_S.js} +1 -1
  141. package/src/assets/web-panel/assets/{placementArrow-CYrM2tyK.js → placementArrow-Bcfs43CX.js} +1 -1
  142. package/src/assets/web-panel/assets/{responsiveObserve-k_06cQ96.js → responsiveObserve-a9bcC1p-.js} +1 -1
  143. package/src/assets/web-panel/assets/{slide-CPKG0pxS.js → slide-BDTzgOXN.js} +1 -1
  144. package/src/assets/web-panel/assets/{statusUtils-BDDEeLTw.js → statusUtils-Iq1j_HUg.js} +1 -1
  145. package/src/assets/web-panel/assets/{styleChecker-Cmb7RTh7.js → styleChecker-DAZEYxeh.js} +1 -1
  146. package/src/assets/web-panel/assets/{useFlexGapSupport-e2IedC4N.js → useFlexGapSupport-DVkjyDrh.js} +1 -1
  147. package/src/assets/web-panel/assets/{useFs-CE_gxVnm.js → useFs-wXTpNTHt.js} +1 -1
  148. package/src/assets/web-panel/assets/{usePersonalDataHub-BkMIIKMz.js → usePersonalDataHub-CtQM_aZr.js} +1 -1
  149. package/src/assets/web-panel/assets/{vnode-DW886XV1.js → vnode-hjEoG85v.js} +1 -1
  150. package/src/assets/web-panel/assets/{zoom-Dwo_adaf.js → zoom-BHVpyKg_.js} +1 -1
  151. package/src/assets/web-panel/index.html +1 -1
  152. package/src/command-help-index.json +3 -3
  153. package/src/commands/agent.js +14 -2
  154. package/src/commands/artifacts.js +235 -0
  155. package/src/commands/background-session.js +100 -10
  156. package/src/commands/cowork.js +86 -57
  157. package/src/commands/mcp.js +5 -0
  158. package/src/commands/plugin.js +145 -29
  159. package/src/commands/remote-control.js +295 -110
  160. package/src/commands/session-location.js +20 -1
  161. package/src/commands/session-message.js +171 -0
  162. package/src/commands/session.js +75 -69
  163. package/src/data/changelog.json +29 -3
  164. package/src/gateways/ws/message-dispatcher.js +57 -0
  165. package/src/harness/mcp-client.js +141 -10
  166. package/src/lib/artifact-access-ledger.js +1 -0
  167. package/src/lib/artifact-deletion-ledger.js +1 -0
  168. package/src/lib/artifact-recovery.js +970 -0
  169. package/src/lib/artifact-workbench-projection.js +171 -0
  170. package/src/lib/background-session-transport.js +153 -27
  171. package/src/lib/dynamic-workflow-runtime.js +408 -1
  172. package/src/lib/execution-location-target.js +4 -2
  173. package/src/lib/mcp-client.js +2 -0
  174. package/src/lib/mcp-headers-helper.js +47 -5
  175. package/src/lib/mcp-oauth.js +55 -6
  176. package/src/lib/mcp-stdio-executable-identity.js +16 -0
  177. package/src/lib/mcp-stdio-native-code-policy.js +74 -0
  178. package/src/lib/mcp-stdio-package-materialization.js +12 -3
  179. package/src/lib/mcp-tls.js +230 -0
  180. package/src/lib/output-styles.js +12 -0
  181. package/src/lib/plugin-runtime/install.js +614 -71
  182. package/src/lib/plugin-runtime/marketplace-archive-source.js +331 -0
  183. package/src/lib/plugin-runtime/marketplace-catalog.js +52 -6
  184. package/src/lib/plugin-runtime/marketplace-remote-artifacts.js +1 -0
  185. package/src/lib/plugin-runtime/marketplace-source-adapter.js +163 -0
  186. package/src/lib/plugin-runtime/marketplace-source-cache.js +11 -1
  187. package/src/lib/plugin-runtime/remote-source.js +435 -55
  188. package/src/lib/plugin-security.js +393 -41
  189. package/src/lib/plugin-source-identity.js +850 -0
  190. package/src/lib/process-execution-broker/index.js +112 -0
  191. package/src/lib/process-execution-broker/platform-sandbox.js +57 -3
  192. package/src/lib/process-execution-broker/workspace-transaction.js +26 -0
  193. package/src/lib/remote-approval-bridge.js +136 -46
  194. package/src/lib/remote-control.js +217 -31
  195. package/src/lib/session-message-fabric.js +1105 -0
  196. package/src/lib/session-projection.js +171 -12
  197. package/src/lib/session-runtime-retention.js +155 -0
  198. package/src/lib/settings-loader.cjs +29 -0
  199. package/src/repl/agent-repl.js +51 -14
  200. package/src/repl/permission-prompt.js +31 -8
  201. package/src/repl/repl-keybindings.js +55 -0
  202. package/src/runtime/agent-core.js +63 -2
  203. package/src/runtime/headless-runner.js +47 -66
  204. package/src/runtime/headless-stream.js +18 -24
  205. package/src/runtime/mcp-config.js +2 -0
  206. package/src/runtime/policies/agent-policy.js +2 -0
  207. package/src/assets/web-panel/assets/ChatBubbleRenderer-xijcWkJe.js +0 -1
  208. package/src/assets/web-panel/assets/MarkdownRenderer-hDpWdiBc.js +0 -1
  209. package/src/assets/web-panel/assets/Projects-BC9KSDM6.js +0 -1
  210. package/src/assets/web-panel/assets/Providers-Dz-CAYYU.js +0 -1
  211. package/src/assets/web-panel/assets/devWarning-DJZ0gAMu.js +0 -1
  212. package/src/assets/web-panel/assets/index-Bizb4hkw.js +0 -1
  213. package/src/assets/web-panel/assets/index-OHSl-xCh.js +0 -1
  214. package/src/assets/web-panel/assets/parsers-UEvh_ShA.js +0 -4
@@ -10,13 +10,12 @@
10
10
  * cc remote-control status [--json]
11
11
  * cc remote-control stop [--port N]
12
12
  *
13
- * `start` boots the WS server IN-PROCESS, then opens a loopback client that
14
- * acts as the remote session HOST (the registry closes a remote session when
15
- * its host disconnects, so this client stays open for the server's lifetime).
13
+ * `start` boots the WS server IN-PROCESS, then opens a loopback client backed
14
+ * by durable possession-proof membership. A crash detaches transport without
15
+ * silently transferring host authority.
16
16
  * With a relay configured you get the E2EE `chainlesschain://remote-session/…`
17
- * URI; without one you get a direct-LAN pairing URI embedding ws://<lan-ip>.
18
- * Devices join with observe/prompt/approve/interrupt scopes — the same wire
19
- * contract the web-panel and Android client already speak.
17
+ * URI. Without one, the safe default is a loopback-only direct pairing URI;
18
+ * LAN binding, approval, and interruption are separate explicit opt-ins.
20
19
  */
21
20
 
22
21
  import chalk from "chalk";
@@ -24,16 +23,24 @@ import { logger } from "../lib/logger.js";
24
23
  import { loadConfig } from "../lib/config-manager.js";
25
24
  import { createAgentRuntimeFactory } from "../runtime/runtime-factory.js";
26
25
  import { WsRpcClient } from "../lib/ws-rpc-client.js";
26
+ import { RemoteApprovalBridge } from "../lib/remote-approval-bridge.js";
27
27
  import {
28
- buildDirectPairingUri,
28
+ assertDirectWsUrlAllowed,
29
+ isLoopbackBindHost,
29
30
  pickLanAddress,
30
31
  readRemoteControlStates,
31
32
  removeRemoteControlState,
32
33
  renderQrCode,
33
34
  resolveRemoteControlOptions,
35
+ resolveRemoteControlWsUrl,
34
36
  writeRemoteControlState,
35
37
  } from "../lib/remote-control.js";
36
38
 
39
+ function websocketHost(host) {
40
+ const value = String(host || "127.0.0.1");
41
+ return value.includes(":") && !value.startsWith("[") ? `[${value}]` : value;
42
+ }
43
+
37
44
  export function registerRemoteControlCommand(program) {
38
45
  const cmd = program
39
46
  .command("remote-control")
@@ -48,17 +55,23 @@ export function registerRemoteControlCommand(program) {
48
55
  "Start the remote-control host (WS server + remote session) and print the pairing URI/QR",
49
56
  )
50
57
  .option("-p, --port <port>", "WS server port (default 18800)")
51
- .option("--host <host>", "Bind host (default 0.0.0.0)")
58
+ .option("--host <host>", "Bind host (default 127.0.0.1)")
59
+ .option(
60
+ "--allow-lan",
61
+ "DANGEROUS: allow a private/LAN listener (defaults host to 0.0.0.0)",
62
+ )
52
63
  .option("--token <token>", "Server auth token (default: auto-generated)")
53
64
  .option(
54
65
  "--relay-url <url>",
55
- "Signaling relay URL for E2EE pairing (falls back to direct LAN pairing when unset)",
66
+ "Signaling relay URL for outbound E2EE pairing",
56
67
  )
57
68
  .option("--peer-id <id>", "Stable relay peer ID for this host")
58
69
  .option(
59
70
  "--scopes <csv>",
60
- "Granted device scopes (observe,prompt,approve,interrupt)",
71
+ "Base device scopes (default observe,prompt; privileged scopes need their opt-ins)",
61
72
  )
73
+ .option("--allow-approve", "Grant paired devices the approve scope")
74
+ .option("--allow-interrupt", "Grant paired devices the interrupt scope")
62
75
  .option("--name <name>", "Remote session display name")
63
76
  .option(
64
77
  "--session <agentSessionId>",
@@ -90,6 +103,37 @@ export function registerRemoteControlCommand(program) {
90
103
  });
91
104
  }
92
105
 
106
+ /** Preserve an exact validated bind host instead of broadening it via serve. */
107
+ export function buildRemoteControlServerOptions(resolved) {
108
+ return {
109
+ port: resolved.port,
110
+ host: resolved.host,
111
+ token: resolved.token,
112
+ allowRemote: false,
113
+ remoteSessionRelayUrl: resolved.relayUrl,
114
+ remoteSessionPeerId: resolved.peerId,
115
+ };
116
+ }
117
+
118
+ function redactStatusUrl(value) {
119
+ if (!value) return value || null;
120
+ try {
121
+ const parsed = new URL(String(value));
122
+ const hadCredentials = Boolean(parsed.username || parsed.password);
123
+ const hadQuery = Boolean(parsed.search);
124
+ parsed.username = "";
125
+ parsed.password = "";
126
+ parsed.search = "";
127
+ parsed.hash = "";
128
+ return (
129
+ parsed.toString() +
130
+ (hadCredentials || hadQuery ? "[credentials/query redacted]" : "")
131
+ );
132
+ } catch {
133
+ return "[invalid URL redacted]";
134
+ }
135
+ }
136
+
93
137
  /**
94
138
  * Boot the host: server runtime + loopback host client + remote session +
95
139
  * pairing printout + discovery state. Resolves `{code}` (0 = serving; the
@@ -99,6 +143,7 @@ export function registerRemoteControlCommand(program) {
99
143
  export async function runRemoteControlStart(options = {}, _deps = {}) {
100
144
  const log = _deps.log || ((m) => console.log(m));
101
145
  const errLog = _deps.err || ((m) => logger.error(m));
146
+ const warnLog = _deps.warn || ((m) => logger.warn(m));
102
147
  const env = _deps.env || process.env;
103
148
  const stateDir = _deps.stateDir; // undefined → default dir
104
149
  let config = {};
@@ -114,6 +159,9 @@ export async function runRemoteControlStart(options = {}, _deps = {}) {
114
159
  flags: {
115
160
  port: options.port,
116
161
  host: options.host,
162
+ allowLan: options.allowLan,
163
+ allowApprove: options.allowApprove,
164
+ allowInterrupt: options.allowInterrupt,
117
165
  token: options.token,
118
166
  relayUrl: options.relayUrl,
119
167
  peerId: options.peerId,
@@ -127,6 +175,40 @@ export async function runRemoteControlStart(options = {}, _deps = {}) {
127
175
  errLog(err.message);
128
176
  return { code: 4 };
129
177
  }
178
+ const localControlHost =
179
+ resolved.host === "0.0.0.0"
180
+ ? "127.0.0.1"
181
+ : resolved.host === "::"
182
+ ? "::1"
183
+ : resolved.host;
184
+ const localControlUrl = `ws://${websocketHost(localControlHost)}:${resolved.port}`;
185
+
186
+ for (const warning of resolved.warnings) warnLog(warning);
187
+ if (resolved.lanAccessible) {
188
+ warnLog(
189
+ `LAN access enabled on ${resolved.host}:${resolved.port}. This exposes the ` +
190
+ "authenticated WebSocket endpoint to the local network; use only on a trusted network." +
191
+ (resolved.relayUrl
192
+ ? ""
193
+ : " Direct pairing uses plaintext ws:// with bearer credentials; prefer --relay-url when available."),
194
+ );
195
+ }
196
+ const lanAddress = resolved.lanAccessible
197
+ ? _deps.lanAddress ||
198
+ (_deps.pickLanAddress ? _deps.pickLanAddress() : pickLanAddress())
199
+ : null;
200
+ const preflightWsUrl = resolveRemoteControlWsUrl(resolved, { lanAddress });
201
+ if (
202
+ !resolved.relayUrl &&
203
+ resolved.lanAccessible &&
204
+ isLoopbackBindHost(new URL(preflightWsUrl).hostname)
205
+ ) {
206
+ errLog(
207
+ "LAN binding is enabled but no private LAN address can be advertised; " +
208
+ "use --host with a private interface address or configure a relay",
209
+ );
210
+ return { code: 4 };
211
+ }
130
212
 
131
213
  // Refuse to double-start on a port that already has a LIVE host.
132
214
  const existing = readRemoteControlStates({ dir: stateDir }).find(
@@ -146,14 +228,7 @@ export async function runRemoteControlStart(options = {}, _deps = {}) {
146
228
  _deps.startServer ||
147
229
  (async () =>
148
230
  createAgentRuntimeFactory()
149
- .createServerRuntime({
150
- port: resolved.port,
151
- host: resolved.host,
152
- token: resolved.token,
153
- allowRemote: resolved.host !== "127.0.0.1",
154
- remoteSessionRelayUrl: resolved.relayUrl,
155
- remoteSessionPeerId: resolved.peerId,
156
- })
231
+ .createServerRuntime(buildRemoteControlServerOptions(resolved))
157
232
  .startServer());
158
233
  server = await startServer(resolved);
159
234
  } catch (err) {
@@ -161,19 +236,19 @@ export async function runRemoteControlStart(options = {}, _deps = {}) {
161
236
  return { code: 1 };
162
237
  }
163
238
 
164
- // Loopback HOST connection — must stay open, its disconnect closes the
165
- // remote session (registry semantics).
166
- const client =
167
- _deps.createClient?.(resolved) ||
168
- new WsRpcClient({ url: `ws://127.0.0.1:${resolved.port}` });
169
- let remoteSession;
170
- let pairing;
239
+ // Create an agent session first when the caller did not select one. This
240
+ // bootstrap client is not the Remote Session host and never receives pairing
241
+ // authority.
242
+ let sessionClient = null;
171
243
  let agentSessionId = options.session || null;
172
244
  try {
173
- await client.connect();
174
- await client.auth(resolved.token);
175
245
  if (!agentSessionId) {
176
- const created = await client.request("session-create", {
246
+ sessionClient = new WsRpcClient({
247
+ url: localControlUrl,
248
+ });
249
+ await sessionClient.connect();
250
+ await sessionClient.auth(resolved.token);
251
+ const created = await sessionClient.request("session-create", {
177
252
  sessionType: "agent",
178
253
  projectRoot: _deps.cwd || process.cwd(),
179
254
  });
@@ -186,16 +261,42 @@ export async function runRemoteControlStart(options = {}, _deps = {}) {
186
261
  throw new Error("session-create returned no sessionId");
187
262
  }
188
263
  }
189
- const createdRemote = await client.request("remote-session-create", {
190
- sessionId: agentSessionId,
264
+ } catch (err) {
265
+ errLog(`Failed to create agent session: ${err.message}`);
266
+ sessionClient?.close();
267
+ try {
268
+ await server.stop?.();
269
+ } catch {
270
+ // best-effort teardown
271
+ }
272
+ return { code: 1 };
273
+ } finally {
274
+ sessionClient?.close();
275
+ }
276
+
277
+ // Standalone start uses the same durable, possession-proof membership host
278
+ // as headless/REPL approvals. A crash detaches the transport; restarting with
279
+ // the same agent session resumes or re-enables the durable membership.
280
+ const createBridge =
281
+ _deps.createBridge ||
282
+ ((bridgeOptions) => new RemoteApprovalBridge(bridgeOptions));
283
+ let bridge;
284
+ try {
285
+ bridge = createBridge({
286
+ wsUrl: localControlUrl,
287
+ token: resolved.token,
288
+ agentSessionId,
191
289
  name: resolved.name,
192
290
  scopes: resolved.scopes,
291
+ approvalStateFile: _deps.approvalStateFile,
292
+ membershipHostStateFile: _deps.membershipHostStateFile,
293
+ membershipHostWitnessFile: _deps.membershipHostWitnessFile,
294
+ membershipHostAuthorityLockFile: _deps.membershipHostAuthorityLockFile,
193
295
  });
194
- remoteSession = createdRemote.session;
195
- pairing = createdRemote.pairing;
296
+ await bridge.start();
196
297
  } catch (err) {
197
- errLog(`Failed to create remote session: ${err.message}`);
198
- client.close();
298
+ errLog(`Failed to create durable remote session: ${err.message}`);
299
+ await bridge?.close?.().catch(() => undefined);
199
300
  try {
200
301
  await server.stop?.();
201
302
  } catch {
@@ -203,37 +304,63 @@ export async function runRemoteControlStart(options = {}, _deps = {}) {
203
304
  }
204
305
  return { code: 1 };
205
306
  }
307
+ const client = bridge.client;
308
+ const pairing = bridge.pairing;
309
+ const remoteSession = {
310
+ sessionId: bridge.remoteSessionId,
311
+ };
206
312
 
207
- const relayMode = Boolean(pairing?.uri);
208
- const lanAddress = _deps.lanAddress || pickLanAddress() || "127.0.0.1";
209
- const wsUrl = `ws://${lanAddress}:${resolved.port}`;
210
- const pairingUri = relayMode
211
- ? pairing.uri
212
- : buildDirectPairingUri({
213
- wsUrl,
214
- serverToken: resolved.token,
215
- remoteSessionId: remoteSession.sessionId,
216
- agentSessionId,
217
- pairingToken: pairing.token,
218
- scopes: pairing.scopes,
219
- expiresAt: pairing.expiresAt,
220
- });
313
+ let relayMode;
314
+ let wsUrl;
315
+ let pairingUri;
316
+ try {
317
+ relayMode = Boolean(pairing?.uri);
318
+ if (resolved.relayUrl && !relayMode) {
319
+ throw new Error(
320
+ "Relay pairing was requested but no relay URI was issued; refusing direct fallback",
321
+ );
322
+ }
323
+ wsUrl = resolveRemoteControlWsUrl(resolved, { lanAddress });
324
+ if (!relayMode) assertDirectWsUrlAllowed(wsUrl);
325
+ pairingUri = bridge.pairingInfo({ lanWsUrl: wsUrl })?.uri;
326
+ if (!pairingUri) throw new Error("Remote session returned no pairing URI");
327
+ } catch (err) {
328
+ await bridge.close?.().catch(() => undefined);
329
+ await server.stop?.().catch(() => undefined);
330
+ errLog(`Failed to finish remote-control startup: ${err.message}`);
331
+ return { code: 1 };
332
+ }
221
333
 
222
334
  const state = {
223
335
  pid: process.pid,
224
336
  port: resolved.port,
225
337
  host: resolved.host,
226
338
  wsUrl,
227
- token: resolved.token,
228
339
  mode: relayMode ? "relay" : "direct",
229
- relayUrl: resolved.relayUrl || null,
340
+ exposure: relayMode
341
+ ? "outbound-relay"
342
+ : resolved.lanAccessible
343
+ ? "direct-lan"
344
+ : "loopback",
345
+ lanAccessible: resolved.lanAccessible,
346
+ relayUrl: redactStatusUrl(resolved.relayUrl),
230
347
  peerId: resolved.peerId || null,
231
348
  agentSessionId,
232
349
  remoteSessionId: remoteSession.sessionId,
233
350
  scopes: pairing.scopes,
234
351
  startedAt: new Date().toISOString(),
235
352
  };
236
- const stateFile = writeRemoteControlState(state, { dir: stateDir });
353
+ let stateFile;
354
+ try {
355
+ stateFile = (_deps.writeState || writeRemoteControlState)(state, {
356
+ dir: stateDir,
357
+ });
358
+ } catch (err) {
359
+ await bridge.close?.().catch(() => undefined);
360
+ await server.stop?.().catch(() => undefined);
361
+ errLog(`Failed to finish remote-control startup: ${err.message}`);
362
+ return { code: 1 };
363
+ }
237
364
  const cleanup = () => {
238
365
  removeRemoteControlState(resolved.port, { dir: stateDir });
239
366
  };
@@ -241,70 +368,102 @@ export async function runRemoteControlStart(options = {}, _deps = {}) {
241
368
  process.once("SIGINT", cleanup);
242
369
  process.once("SIGTERM", cleanup);
243
370
 
244
- if (options.json) {
245
- log(
246
- JSON.stringify(
247
- {
248
- mode: state.mode,
249
- wsUrl,
250
- port: resolved.port,
251
- agentSessionId,
252
- remoteSessionId: remoteSession.sessionId,
253
- pairingUri,
254
- pairing: {
255
- token: pairing.token,
256
- scopes: pairing.scopes,
257
- expiresAt: pairing.expiresAt,
371
+ try {
372
+ if (options.json) {
373
+ log(
374
+ JSON.stringify(
375
+ {
376
+ mode: state.mode,
377
+ exposure: state.exposure,
378
+ host: resolved.host,
379
+ lanAccessible: resolved.lanAccessible,
380
+ wsUrl,
381
+ port: resolved.port,
382
+ agentSessionId,
383
+ remoteSessionId: remoteSession.sessionId,
384
+ pairingUri,
385
+ pairing: {
386
+ token: pairing.token,
387
+ scopes: pairing.scopes,
388
+ expiresAt: pairing.expiresAt,
389
+ },
390
+ stateFile,
258
391
  },
259
- stateFile,
260
- },
261
- null,
262
- 2,
263
- ),
264
- );
265
- } else {
266
- log("");
267
- log(chalk.bold(" Remote Control ready"));
268
- log("");
269
- log(
270
- ` Mode: ${state.mode === "relay" ? chalk.cyan("relay (E2EE)") : chalk.cyan("direct LAN")}`,
271
- );
272
- log(` WS server: ${chalk.cyan(wsUrl)}`);
273
- log(` Agent sess: ${agentSessionId}`);
274
- log(` Remote sess: ${remoteSession.sessionId}`);
275
- log(` Scopes: ${pairing.scopes.join(", ")}`);
276
- log(
277
- ` Token exp: ${pairing.expiresAt ? new Date(pairing.expiresAt).toISOString() : "n/a"} ${chalk.dim("(one-time; re-run start for another device)")}`,
278
- );
279
- log("");
280
- log(" Pairing URI (open in ChainlessChain mobile / paste in web panel):");
281
- log("");
282
- log(" " + chalk.green(pairingUri));
283
- if (options.qr !== false) {
284
- const qr = await renderQrCode(pairingUri, _deps);
285
- if (qr) {
286
- log("");
287
- log(
288
- qr
289
- .split("\n")
290
- .map((line) => " " + line)
291
- .join("\n"),
292
- );
293
- } else {
392
+ null,
393
+ 2,
394
+ ),
395
+ );
396
+ } else {
397
+ log("");
398
+ log(chalk.bold(" Remote Control ready"));
399
+ log("");
400
+ log(
401
+ ` Mode: ${
402
+ state.mode === "relay"
403
+ ? chalk.cyan("relay (E2EE, outbound)")
404
+ : state.exposure === "direct-lan"
405
+ ? chalk.yellow("direct LAN (explicit opt-in)")
406
+ : chalk.cyan("direct loopback")
407
+ }`,
408
+ );
409
+ log(` WS server: ${chalk.cyan(wsUrl)}`);
410
+ log(` Agent sess: ${agentSessionId}`);
411
+ log(` Remote sess: ${remoteSession.sessionId}`);
412
+ log(` Scopes: ${pairing.scopes.join(", ")}`);
413
+ log(
414
+ ` Token exp: ${pairing.expiresAt ? new Date(pairing.expiresAt).toISOString() : "n/a"} ${chalk.dim("(one-time; re-run start for another device)")}`,
415
+ );
416
+ log("");
417
+ log(
418
+ relayMode || resolved.lanAccessible
419
+ ? " Pairing URI (open in ChainlessChain mobile / paste in web panel):"
420
+ : " Pairing URI (local clients on this machine only):",
421
+ );
422
+ log("");
423
+ log(" " + chalk.green(pairingUri));
424
+ if (!relayMode && !resolved.lanAccessible) {
294
425
  log("");
295
426
  log(
296
- chalk.dim(
297
- " (install the optional `qrcode` package for a terminal QR)",
427
+ chalk.yellow(
428
+ " Loopback only: use --allow-lan on a trusted network or configure --relay-url for another device.",
298
429
  ),
299
430
  );
300
431
  }
432
+ if (options.qr !== false && (relayMode || resolved.lanAccessible)) {
433
+ const qr = await renderQrCode(pairingUri, _deps);
434
+ if (qr) {
435
+ log("");
436
+ log(
437
+ qr
438
+ .split("\n")
439
+ .map((line) => " " + line)
440
+ .join("\n"),
441
+ );
442
+ } else {
443
+ log("");
444
+ log(
445
+ chalk.dim(
446
+ " (install the optional `qrcode` package for a terminal QR)",
447
+ ),
448
+ );
449
+ }
450
+ }
451
+ log("");
452
+ log(chalk.dim(" Press Ctrl+C to stop"));
453
+ log("");
301
454
  }
302
- log("");
303
- log(chalk.dim(" Press Ctrl+C to stop"));
304
- log("");
455
+ } catch (err) {
456
+ process.removeListener("exit", cleanup);
457
+ process.removeListener("SIGINT", cleanup);
458
+ process.removeListener("SIGTERM", cleanup);
459
+ cleanup();
460
+ await bridge.close?.().catch(() => undefined);
461
+ await server.stop?.().catch(() => undefined);
462
+ errLog(`Failed to finish remote-control startup: ${err.message}`);
463
+ return { code: 1 };
305
464
  }
306
465
 
307
- return { code: 0, client, server, state, pairingUri };
466
+ return { code: 0, client, bridge, server, state, pairingUri };
308
467
  }
309
468
 
310
469
  export function runRemoteControlStatus(options = {}, _deps = {}) {
@@ -320,9 +479,20 @@ export function runRemoteControlStatus(options = {}, _deps = {}) {
320
479
  if (options.json) {
321
480
  log(
322
481
  JSON.stringify(
323
- // The token stays in the 0600 state file — status output may be pasted
324
- // into issues/chat, so redact it here.
325
- states.map(({ token: _token, ...rest }) => rest),
482
+ // Defense in depth for legacy state files written before tokens were
483
+ // removed from discovery state.
484
+ states.map(
485
+ ({
486
+ token: _token,
487
+ serverToken: _serverToken,
488
+ pairingToken: _pairingToken,
489
+ pairingUri: _pairingUri,
490
+ ...rest
491
+ }) => ({
492
+ ...rest,
493
+ relayUrl: redactStatusUrl(rest.relayUrl),
494
+ }),
495
+ ),
326
496
  null,
327
497
  2,
328
498
  ),
@@ -343,7 +513,22 @@ export function runRemoteControlStatus(options = {}, _deps = {}) {
343
513
  const badge = state.alive ? chalk.green("running") : chalk.yellow("stale");
344
514
  log(
345
515
  ` ${badge} port ${String(state.port).padEnd(6)} pid ${String(state.pid).padEnd(8)} ` +
346
- `${(state.mode || "?").padEnd(7)} session ${state.agentSessionId || "?"}`,
516
+ `${(state.mode || "?").padEnd(7)} ${(state.exposure || "?").padEnd(18)} ` +
517
+ `host ${state.host || "?"} session ${state.agentSessionId || "?"}`,
518
+ );
519
+ }
520
+ if (
521
+ states.some(
522
+ (state) =>
523
+ state.alive &&
524
+ state.legacyExposure &&
525
+ state.exposure === "legacy-unknown-lan",
526
+ )
527
+ ) {
528
+ log(
529
+ chalk.red(
530
+ " Warning: a legacy host may still be LAN-exposed. Stop and restart it to apply the loopback default.",
531
+ ),
347
532
  );
348
533
  }
349
534
  log("");
@@ -5,6 +5,7 @@ import {
5
5
  buildExecutionLocationCatalog,
6
6
  buildExecutionLocationHandoffPreview,
7
7
  computeExecutionLocationTargetFactsDigest,
8
+ createExecutionLocationTargetAttestation,
8
9
  } from "../lib/execution-location-contract.js";
9
10
  import { captureAmbientExecutionLocation } from "../lib/execution-location-runtime.js";
10
11
  import {
@@ -333,6 +334,24 @@ export function prepareSessionReplicaHandoff(sessionId, options, deps = {}) {
333
334
  if (targetFactsDigest !== String(options.expectedTargetFactsDigest || "")) {
334
335
  throw new Error("target facts changed before location handoff append");
335
336
  }
337
+ if (!/^sha256:[a-f0-9]{64}$/u.test(String(options.attestationDigest || ""))) {
338
+ throw new Error("source target attestation digest is invalid");
339
+ }
340
+ // observedAt is intentionally excluded from targetFactsDigest but included
341
+ // in attestationDigest. The source-side probe and this target-side prepare
342
+ // are separate process invocations, so a real target can never reproduce
343
+ // the source probe's timestamp. Bind the handoff event to this fresh target
344
+ // observation; the source rehashes its receipt and immediately verifies the
345
+ // canonical target projection before it is allowed to resume.
346
+ const targetAttestation = createExecutionLocationTargetAttestation({
347
+ profileDigest: options.profileDigest,
348
+ sourceSessionId: sessionId,
349
+ sourceHeadHash: options.expectedHeadHash,
350
+ sourceEventCount: Number(options.expectedEventCount),
351
+ targetEvidenceId: options.targetEvidenceId,
352
+ baseCommit: binding.source.git.commit,
353
+ binding,
354
+ });
336
355
  return (
337
356
  deps.installSessionReplicaWithLocationHandoff ||
338
357
  installSessionReplicaWithLocationHandoff
@@ -348,7 +367,7 @@ export function prepareSessionReplicaHandoff(sessionId, options, deps = {}) {
348
367
  profileDigest: options.profileDigest,
349
368
  targetEvidenceId: options.targetEvidenceId,
350
369
  targetFactsDigest,
351
- attestationDigest: options.attestationDigest,
370
+ attestationDigest: targetAttestation.attestationDigest,
352
371
  binding,
353
372
  },
354
373
  );