chainlesschain 0.164.0 → 0.165.1

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 (189) hide show
  1. package/package.json +4 -4
  2. package/src/assets/web-panel/.build-hash +1 -1
  3. package/src/assets/web-panel/assets/{AIOps-D2MEn7fI.js → AIOps-CYxFDrcm.js} +1 -1
  4. package/src/assets/web-panel/assets/{ActionButton-lCW_4Gf0.js → ActionButton-CPBtdiU2.js} +1 -1
  5. package/src/assets/web-panel/assets/{Analytics-Bs7i-nsS.js → Analytics-Dayq7Dp7.js} +3 -3
  6. package/src/assets/web-panel/assets/{AppLayout-CK7B6S_j.js → AppLayout-6U7bPb2s.js} +5 -5
  7. package/src/assets/web-panel/assets/{Artifacts-C8ILjpyo.js → Artifacts-D2Iqg_3D.js} +1 -1
  8. package/src/assets/web-panel/assets/{Audit-Bb9cNaWc.js → Audit-CdoOB7WV.js} +1 -1
  9. package/src/assets/web-panel/assets/{BackgroundAgents-sC9ooXIf.js → BackgroundAgents-DrnAVB8w.js} +1 -1
  10. package/src/assets/web-panel/assets/{Backup-D-DrsplT.js → Backup-CesMi5cy.js} +1 -1
  11. package/src/assets/web-panel/assets/{BaseInput-uCNZpwSK.js → BaseInput-DaX9ptax.js} +1 -1
  12. package/src/assets/web-panel/assets/{Chat-CsDK1uE9.js → Chat-OrwOPxDS.js} +6 -6
  13. package/src/assets/web-panel/assets/{ChatBubbleRenderer-OqSDt50w.js → ChatBubbleRenderer-CvPZFRxM.js} +1 -1
  14. package/src/assets/web-panel/assets/{Checkbox-JhTXM1-K.js → Checkbox-DiHbcysC.js} +1 -1
  15. package/src/assets/web-panel/assets/{Codegen-BbzZLnyD.js → Codegen-CbgCkaBJ.js} +1 -1
  16. package/src/assets/web-panel/assets/{Col-D4VW4m4j.js → Col-Du5hgrh4.js} +1 -1
  17. package/src/assets/web-panel/assets/{Community-BXrYawxV.js → Community-DsPOBW-p.js} +1 -1
  18. package/src/assets/web-panel/assets/{Compact-DCihGy1u.js → Compact-BwHIFkNC.js} +1 -1
  19. package/src/assets/web-panel/assets/{Compliance-DlyZ-lk8.js → Compliance-BvrDQSgd.js} +1 -1
  20. package/src/assets/web-panel/assets/{Cowork-DvGZrhbO.js → Cowork-gwAE6arx.js} +2 -2
  21. package/src/assets/web-panel/assets/{Cron-DUncZi8h.js → Cron-DW-0l4uF.js} +2 -2
  22. package/src/assets/web-panel/assets/{Crosschain-C_hLuUcm.js → Crosschain-j-NsLesB.js} +1 -1
  23. package/src/assets/web-panel/assets/{DID-b6YSUqxr.js → DID-CibVEBOI.js} +2 -2
  24. package/src/assets/web-panel/assets/{Dashboard-uvM15Cfb.js → Dashboard-W4z9vSfe.js} +2 -2
  25. package/src/assets/web-panel/assets/{Dropdown-D1-Nd4qY.js → Dropdown-DAcrkKxG.js} +1 -1
  26. package/src/assets/web-panel/assets/{EmailListRenderer-8ApvL6Zc.js → EmailListRenderer-6GXah-89.js} +1 -1
  27. package/src/assets/web-panel/assets/{FamilyGuardDashboard-LYJt8811.js → FamilyGuardDashboard-TKDCq8Ku.js} +1 -1
  28. package/src/assets/web-panel/assets/{Federation-BbI3HGpv.js → Federation-BXBLlcNm.js} +1 -1
  29. package/src/assets/web-panel/assets/{FormItemContext-DSoEKuFu.js → FormItemContext-BoJJBkG3.js} +1 -1
  30. package/src/assets/web-panel/assets/{GenericCardRenderer-B0S9OWd5.js → GenericCardRenderer-okjsl46v.js} +1 -1
  31. package/src/assets/web-panel/assets/{Git-DIs9Zhf3.js → Git-Rvs7i6nn.js} +2 -2
  32. package/src/assets/web-panel/assets/{Governance-jzZXrxcl.js → Governance-CHSUxXXS.js} +1 -1
  33. package/src/assets/web-panel/assets/{Inference-JUPjf2ZI.js → Inference-V3BlfxsB.js} +1 -1
  34. package/src/assets/web-panel/assets/{KnowledgeGraph-DxnfJvlP.js → KnowledgeGraph-C_NlD4aR.js} +1 -1
  35. package/src/assets/web-panel/assets/{Logs-iXiz8BEE.js → Logs-Nn4LNxhA.js} +2 -2
  36. package/src/assets/web-panel/assets/{MarkdownRenderer-GmB6DOr6.js → MarkdownRenderer-SVAgNnkt.js} +1 -1
  37. package/src/assets/web-panel/assets/{Marketplace-Y3Xch312.js → Marketplace-DEmjFcg-.js} +1 -1
  38. package/src/assets/web-panel/assets/{McpTools-D7kGGbO7.js → McpTools-DlopXipI.js} +3 -3
  39. package/src/assets/web-panel/assets/{Memory-Do_hVxzh.js → Memory-BG3ynX60.js} +2 -2
  40. package/src/assets/web-panel/assets/{MobileBridge-CtrR0Tas.js → MobileBridge-DeRzWOfT.js} +1 -1
  41. package/src/assets/web-panel/assets/MobileProjects-Tyd3o2A0.js +1 -0
  42. package/src/assets/web-panel/assets/{Mtc-qo5QCTGd.js → Mtc-BoPbOv1C.js} +2 -2
  43. package/src/assets/web-panel/assets/{MtcAudit-cq5mnjYj.js → MtcAudit-un-rTHp7.js} +2 -2
  44. package/src/assets/web-panel/assets/{Multisig-Bq0MAQHw.js → Multisig-Ds0oveEB.js} +3 -3
  45. package/src/assets/web-panel/assets/{NLProgramming-DdWE6Vi2.js → NLProgramming-D09JI-yY.js} +1 -1
  46. package/src/assets/web-panel/assets/{Notes-BxMBFcIp.js → Notes-DTRhiKJn.js} +3 -3
  47. package/src/assets/web-panel/assets/{NotificationSettings-RTrAcbUe.js → NotificationSettings-BAnnNCim.js} +1 -1
  48. package/src/assets/web-panel/assets/{OrderTableRenderer-DJbpGqzT.js → OrderTableRenderer-Yr-1fSYi.js} +1 -1
  49. package/src/assets/web-panel/assets/{Organization-SQzIPdqC.js → Organization-S9EAolpj.js} +4 -4
  50. package/src/assets/web-panel/assets/{Overflow-02yECh22.js → Overflow-CqcLKqJ8.js} +1 -1
  51. package/src/assets/web-panel/assets/{P2P-BNg4pNCM.js → P2P-Dw0F9EcU.js} +2 -2
  52. package/src/assets/web-panel/assets/{PdhVaultBrowser-BxPJZsD3.js → PdhVaultBrowser-BDRwUevN.js} +3 -3
  53. package/src/assets/web-panel/assets/{Permissions-CLnDnPAz.js → Permissions-D3_c3nKB.js} +4 -4
  54. package/src/assets/web-panel/assets/{PersonalDataHub-C5m0AjGf.js → PersonalDataHub-CxdMV98x.js} +4 -4
  55. package/src/assets/web-panel/assets/{Pipeline-VwOf5Dhv.js → Pipeline-CgjNQFLI.js} +1 -1
  56. package/src/assets/web-panel/assets/{Privacy-CC0W2hZd.js → Privacy-BHghIIht.js} +1 -1
  57. package/src/assets/web-panel/assets/{ProjectInit-Dz893Bob.js → ProjectInit-De6pqaKM.js} +2 -2
  58. package/src/assets/web-panel/assets/{ProjectSettings-CxQkvl0k.js → ProjectSettings-Bg4ZkNYQ.js} +2 -2
  59. package/src/assets/web-panel/assets/{Projects-DDAjkUkT.js → Projects-CP8vPvzs.js} +1 -1
  60. package/src/assets/web-panel/assets/{Providers-DeB9IkxK.js → Providers-CUnHbNf-.js} +1 -1
  61. package/src/assets/web-panel/assets/{QrScannerModal-OCeoaswn.js → QrScannerModal-DA2f3Wts.js} +1 -1
  62. package/src/assets/web-panel/assets/{QuickAsk-DenotaAj.js → QuickAsk-C1FeHDqp.js} +1 -1
  63. package/src/assets/web-panel/assets/{Recommend-Cies_jjI.js → Recommend-B5guaUMN.js} +1 -1
  64. package/src/assets/web-panel/assets/RemoteSession-PDADSds3.js +5 -0
  65. package/src/assets/web-panel/assets/{Reputation-CW0mvxgZ.js → Reputation-eTlRhgvK.js} +1 -1
  66. package/src/assets/web-panel/assets/{Row-SEd32qZU.js → Row-D7AqSGY0.js} +1 -1
  67. package/src/assets/web-panel/assets/{RssFeed-BSwnJ3Lm.js → RssFeed-JiuLm4Ja.js} +3 -3
  68. package/src/assets/web-panel/assets/{Search-xe2m8bkC.js → Search-CwruahW3.js} +1 -1
  69. package/src/assets/web-panel/assets/{Security-Mq2F9Nro.js → Security-CdhqaR0b.js} +4 -4
  70. package/src/assets/web-panel/assets/{Services-BKGw2V3P.js → Services-CU0ZIlcT.js} +2 -2
  71. package/src/assets/web-panel/assets/{Skeleton-DW95-Rxf.js → Skeleton-CtduPfAb.js} +1 -1
  72. package/src/assets/web-panel/assets/{Skills-CHgChCv_.js → Skills-CROXm6AY.js} +1 -1
  73. package/src/assets/web-panel/assets/{Sla-tKh6k3R7.js → Sla-DiZiABFD.js} +1 -1
  74. package/src/assets/web-panel/assets/{SpeechSettings-D6UL3dA5.js → SpeechSettings-B5pLcXUw.js} +1 -1
  75. package/src/assets/web-panel/assets/{SyncSettings-D0Fw8fVL.js → SyncSettings-DBX6J1Oe.js} +2 -2
  76. package/src/assets/web-panel/assets/{Tasks-20x1GB3D.js → Tasks-BLyRJd3-.js} +1 -1
  77. package/src/assets/web-panel/assets/{Templates-DJ-lts-a.js → Templates-BKa5qzmk.js} +1 -1
  78. package/src/assets/web-panel/assets/{Tenant-BFdGpbs7.js → Tenant-DnD3a4M6.js} +1 -1
  79. package/src/assets/web-panel/assets/{Terminal-Xpx61vHL.js → Terminal-DEitQeTb.js} +2 -2
  80. package/src/assets/web-panel/assets/{TimelineRenderer-C6UIvyZQ.js → TimelineRenderer-CjHggTDq.js} +1 -1
  81. package/src/assets/web-panel/assets/{Tokens-Dellq08f.js → Tokens-CsGT7sfl.js} +1 -1
  82. package/src/assets/web-panel/assets/{Trigger-VKw8d0J-.js → Trigger-yr4rFDrW.js} +1 -1
  83. package/src/assets/web-panel/assets/{Trust-DNftdKT5.js → Trust-CLvu1Sjp.js} +1 -1
  84. package/src/assets/web-panel/assets/{UkeySign-DX9VAKJI.js → UkeySign-B0m7yUP8.js} +1 -1
  85. package/src/assets/web-panel/assets/{VideoEditing-B2h3jO8q.js → VideoEditing-C0VvP4lc.js} +1 -1
  86. package/src/assets/web-panel/assets/{Wallet-CjGkof7f.js → Wallet-kllBAG1M.js} +4 -4
  87. package/src/assets/web-panel/assets/{WebAuthn-2Zfcu55P.js → WebAuthn-C54UQNLx.js} +4 -4
  88. package/src/assets/web-panel/assets/{WorkflowEditor-B9eZ8UxZ.js → WorkflowEditor-B7y8qSvU.js} +1 -1
  89. package/src/assets/web-panel/assets/{chat-CQNfD-YO.js → chat-DRzTh8CJ.js} +1 -1
  90. package/src/assets/web-panel/assets/{colors-D4usyFT2.js → colors-BSMwSnCx.js} +1 -1
  91. package/src/assets/web-panel/assets/{compact-item-s4TpjvjX.js → compact-item-BTg602H5.js} +1 -1
  92. package/src/assets/web-panel/assets/{createContext-BRCSvYxj.js → createContext-B6EGsdvp.js} +1 -1
  93. package/src/assets/web-panel/assets/devWarning-Dol8jLEP.js +1 -0
  94. package/src/assets/web-panel/assets/{hasIn-AdkUbkV-.js → hasIn-DDKqJ8RD.js} +1 -1
  95. package/src/assets/web-panel/assets/{index-CKC1t3Ic.js → index-323Hhxxt.js} +1 -1
  96. package/src/assets/web-panel/assets/{index-CkwiSLhU.js → index-BICn3Qh1.js} +1 -1
  97. package/src/assets/web-panel/assets/{index-s_buFMEG.js → index-BMuGd2--.js} +1 -1
  98. package/src/assets/web-panel/assets/{index-MMN9V6JF.js → index-BRqt4fO-.js} +1 -1
  99. package/src/assets/web-panel/assets/{index-DZT36ZYq.js → index-BVTAl5N2.js} +1 -1
  100. package/src/assets/web-panel/assets/{index-Bad4VNvz.js → index-BsR7Y2NG.js} +1 -1
  101. package/src/assets/web-panel/assets/{index-C1mJtE6q.js → index-Bx044qDj.js} +1 -1
  102. package/src/assets/web-panel/assets/{index-CYdoQsa1.js → index-C1GFfuqN.js} +1 -1
  103. package/src/assets/web-panel/assets/index-C3QX5tOl.js +1 -0
  104. package/src/assets/web-panel/assets/{index-CJCFe2Qf.js → index-C66NpWy5.js} +1 -1
  105. package/src/assets/web-panel/assets/{index-D2GjI2oe.js → index-CDTvsX8t.js} +1 -1
  106. package/src/assets/web-panel/assets/{index-C7Y8Aluc.js → index-CGNklbP6.js} +1 -1
  107. package/src/assets/web-panel/assets/{index-DdCB70SS.js → index-CHfLYwcW.js} +1 -1
  108. package/src/assets/web-panel/assets/{index-Bw2MEnUk.js → index-CNxg61YP.js} +1 -1
  109. package/src/assets/web-panel/assets/{index-DARsAyUp.js → index-CQlHFN_g.js} +1 -1
  110. package/src/assets/web-panel/assets/{index-BIoG5yYf.js → index-CUEU48o1.js} +1 -1
  111. package/src/assets/web-panel/assets/{index-DzRjHxEv.js → index-CWGax2QW.js} +1 -1
  112. package/src/assets/web-panel/assets/{index-CCsM_Ybx.js → index-C_JzIXUq.js} +1 -1
  113. package/src/assets/web-panel/assets/{index-B4JJCfQ2.js → index-Cf23wVMs.js} +1 -1
  114. package/src/assets/web-panel/assets/{index-C484EJiS.js → index-CmAInUkX.js} +1 -1
  115. package/src/assets/web-panel/assets/{index-B2V-eKfK.js → index-CmAubjDi.js} +1 -1
  116. package/src/assets/web-panel/assets/{index-D_XBHopw.js → index-Co7oM1qr.js} +1 -1
  117. package/src/assets/web-panel/assets/{index-C5KAQFPQ.js → index-CwPUZbMI.js} +1 -1
  118. package/src/assets/web-panel/assets/{index-BPhkFZ1I.js → index-D1naNj4U.js} +1 -1
  119. package/src/assets/web-panel/assets/{index-CQhAVU5t.js → index-D5CsBShd.js} +1 -1
  120. package/src/assets/web-panel/assets/{index-DVKfQ0Ux.js → index-DBT3YeQ9.js} +1 -1
  121. package/src/assets/web-panel/assets/{index-_IRVsV9X.js → index-DLdrHhML.js} +1 -1
  122. package/src/assets/web-panel/assets/{index-CJdQrZth.js → index-Dc77VR3H.js} +1 -1
  123. package/src/assets/web-panel/assets/{index-Cduu2Hhr.js → index-Dfv2lKu3.js} +1 -1
  124. package/src/assets/web-panel/assets/{index--00U6vXZ.js → index-DiW0sDkz.js} +1 -1
  125. package/src/assets/web-panel/assets/{index-BMcimU-2.js → index-DpTHIRfZ.js} +1 -1
  126. package/src/assets/web-panel/assets/{index-CJVEOa4i.js → index-DqFXGzzC.js} +1 -1
  127. package/src/assets/web-panel/assets/{index-BgiXR_-_.js → index-XFe-1twF.js} +1 -1
  128. package/src/assets/web-panel/assets/{index-BQ3DXBtD.js → index-p2HYn89m.js} +3 -3
  129. package/src/assets/web-panel/assets/{index-Cvyt-zhm.js → index-pqfux8vZ.js} +1 -1
  130. package/src/assets/web-panel/assets/{index-nXyk8GZ9.js → index-rjzk9z1Q.js} +1 -1
  131. package/src/assets/web-panel/assets/{index-RfMr79MW.js → index-th2ihDc0.js} +1 -1
  132. package/src/assets/web-panel/assets/{index-BtobShro.js → index-u2YVld9W.js} +1 -1
  133. package/src/assets/web-panel/assets/index-xYybvEic.js +1 -0
  134. package/src/assets/web-panel/assets/{initDefaultProps-Blmpk7HL.js → initDefaultProps-C3Djh9fa.js} +1 -1
  135. package/src/assets/web-panel/assets/{motion-DPBJMv1n.js → motion-BzqFmbSe.js} +1 -1
  136. package/src/assets/web-panel/assets/{move-ButdzCM7.js → move-DJo_FFaW.js} +1 -1
  137. package/src/assets/web-panel/assets/{mtc-parser-cyRC_QCe.js → mtc-parser-_tlTiBvk.js} +1 -1
  138. package/src/assets/web-panel/assets/{omit-1epSV7hz.js → omit-CB2ZyxUg.js} +1 -1
  139. package/src/assets/web-panel/assets/{pickAttrs-DuPY8xJs.js → pickAttrs-DZwIFD4u.js} +1 -1
  140. package/src/assets/web-panel/assets/{placementArrow-6XZqarhe.js → placementArrow-DxZY0dgk.js} +1 -1
  141. package/src/assets/web-panel/assets/{responsiveObserve-B40pXBWM.js → responsiveObserve-CzzGt7GL.js} +1 -1
  142. package/src/assets/web-panel/assets/{slide-jbZ9Wu4n.js → slide-BaIK52yz.js} +1 -1
  143. package/src/assets/web-panel/assets/{statusUtils-BKQQcVy4.js → statusUtils-3-FHu9_M.js} +1 -1
  144. package/src/assets/web-panel/assets/{styleChecker-NiKfWjUT.js → styleChecker-BkRT68fK.js} +1 -1
  145. package/src/assets/web-panel/assets/{useFlexGapSupport-bQUCvPeb.js → useFlexGapSupport-DHYwKS-s.js} +1 -1
  146. package/src/assets/web-panel/assets/{useFs-CUGji7ph.js → useFs-DVVUhV8p.js} +1 -1
  147. package/src/assets/web-panel/assets/{usePersonalDataHub-4veyN2N2.js → usePersonalDataHub-CkrFKAzQ.js} +1 -1
  148. package/src/assets/web-panel/assets/{vnode-1-nLug-Z.js → vnode-BuBLFz0c.js} +1 -1
  149. package/src/assets/web-panel/assets/{zoom-dhCeRDMo.js → zoom-kpm6LtXU.js} +1 -1
  150. package/src/assets/web-panel/index.html +1 -1
  151. package/src/commands/cowork.js +283 -61
  152. package/src/data/changelog.json +15 -2
  153. package/src/gateways/ws/action-protocol.js +293 -19
  154. package/src/gateways/ws/message-dispatcher.js +30 -0
  155. package/src/gateways/ws/remote-session-protocol.js +542 -35
  156. package/src/gateways/ws/ws-approval-gate.js +5 -0
  157. package/src/gateways/ws/ws-server.js +462 -38
  158. package/src/harness/remote-session-audit.js +1 -0
  159. package/src/harness/remote-session-crypto.js +750 -50
  160. package/src/harness/remote-session-registry.js +795 -27
  161. package/src/lib/auto-mode-config.js +98 -3
  162. package/src/lib/background-agent-keeper-protocol.js +154 -10
  163. package/src/lib/background-agent-supervisor.js +840 -48
  164. package/src/lib/background-session-transport.js +23 -6
  165. package/src/lib/background-turn-bootstrap-protocol.js +64 -0
  166. package/src/lib/cowork-workflow.js +853 -61
  167. package/src/lib/delivery-production-adapter.js +38 -2
  168. package/src/lib/dynamic-workflow-facade.js +1105 -17
  169. package/src/lib/mcp-stdio-package-materialization.js +1 -1
  170. package/src/lib/remote-approval-bridge.js +745 -108
  171. package/src/lib/remote-control.js +2 -0
  172. package/src/lib/remote-membership-coordinator.js +604 -40
  173. package/src/lib/remote-membership-host-store.js +59 -10
  174. package/src/lib/runtime-provenance-ledger.js +171 -46
  175. package/src/lib/shell-approval.js +169 -6
  176. package/src/lib/with-file-lock.js +73 -38
  177. package/src/repl/agent-repl.js +70 -29
  178. package/src/repl/remote-approval.js +29 -7
  179. package/src/runtime/agent-core.js +1099 -152
  180. package/src/runtime/headless-runner.js +22 -5
  181. package/src/runtime/headless-stream.js +19 -3
  182. package/src/workers/background-agent-keeper.js +249 -58
  183. package/src/workers/background-agent-worker.js +7 -0
  184. package/src/workers/background-turn-bootstrap.js +29 -2
  185. package/src/assets/web-panel/assets/MobileProjects-BL0Czcoj.js +0 -1
  186. package/src/assets/web-panel/assets/RemoteSession-Dxzl5n2T.js +0 -5
  187. package/src/assets/web-panel/assets/devWarning-DKJjSWRn.js +0 -1
  188. package/src/assets/web-panel/assets/index-C61cnl3K.js +0 -1
  189. package/src/assets/web-panel/assets/index-D4XFHxBr.js +0 -1
@@ -1460,6 +1460,8 @@ async function runAgentHeadlessInWorkspace(
1460
1460
  if (!hermeticExecution) {
1461
1461
  try {
1462
1462
  approvalGate = await getApprovalGate();
1463
+ approvalGate =
1464
+ approvalGate?.createSessionScope?.(sessionId) || approvalGate;
1463
1465
  if (approvalGate && (options.permissionMode || "default") === "auto") {
1464
1466
  // autoMode.decisions: user-configured riskLevel → allow/ask/deny
1465
1467
  // classifier. Only wrap when settings actually customize the map so the
@@ -1483,6 +1485,7 @@ async function runAgentHeadlessInWorkspace(
1483
1485
  }
1484
1486
  }
1485
1487
  if (approvalGate) {
1488
+ approvalGate.setAuthorizationConsumer?.(null);
1486
1489
  if (typeof approvalGate.setSessionPolicy === "function") {
1487
1490
  approvalGate.setSessionPolicy(sessionId, perm.sessionPolicy);
1488
1491
  }
@@ -1980,6 +1983,18 @@ async function runAgentHeadlessInWorkspace(
1980
1983
  !options.permissionPromptTool
1981
1984
  ) {
1982
1985
  try {
1986
+ if (
1987
+ !approvalGate ||
1988
+ typeof approvalGate.setConfirmer !== "function" ||
1989
+ typeof approvalGate.setAuthorizationConsumer !== "function" ||
1990
+ typeof approvalGate.consumeAuthorization !== "function"
1991
+ ) {
1992
+ const error = new Error(
1993
+ "installed session-core cannot bind and consume durable remote approval",
1994
+ );
1995
+ error.code = "CC_REMOTE_APPROVAL_GATE_UNAVAILABLE";
1996
+ throw error;
1997
+ }
1983
1998
  const { startHeadlessRemoteApproval } =
1984
1999
  await import("../lib/remote-approval-bridge.js");
1985
2000
  _remoteApproval = await (
@@ -1989,11 +2004,12 @@ async function runAgentHeadlessInWorkspace(
1989
2004
  writeErr,
1990
2005
  isText,
1991
2006
  });
1992
- if (approvalGate && typeof approvalGate.setConfirmer === "function") {
1993
- approvalGate.setConfirmer(
1994
- _bgPhase.wrapConfirmer(_remoteApproval.confirmer),
1995
- );
1996
- }
2007
+ approvalGate.setConfirmer(
2008
+ _bgPhase.wrapConfirmer(_remoteApproval.confirmer),
2009
+ );
2010
+ approvalGate.setAuthorizationConsumer(
2011
+ _remoteApproval.consumeAuthorization,
2012
+ );
1997
2013
  } catch (err) {
1998
2014
  // Remote approval could not come up → keep headless fail-closed rather
1999
2015
  // than running un-gated; say why so the user can fix pairing.
@@ -3274,6 +3290,7 @@ async function runAgentHeadlessInWorkspace(
3274
3290
  await cleanupDeadline.run("remote-approval", () =>
3275
3291
  _remoteApproval?.close?.(),
3276
3292
  );
3293
+ approvalGate?.setAuthorizationConsumer?.(null);
3277
3294
  // settings.json Stop + SessionEnd hooks when the run finishes. Stop is the
3278
3295
  // canonical async-hook trigger ("run the test suite at turn end"); fire its
3279
3296
  // async hooks fire-and-forget, then settle so a fast background check can
@@ -2059,6 +2059,8 @@ async function runAgentHeadlessStreamInWorkspace(
2059
2059
  let approvalGate = null;
2060
2060
  try {
2061
2061
  approvalGate = await getApprovalGate();
2062
+ approvalGate =
2063
+ approvalGate?.createSessionScope?.(sessionId) || approvalGate;
2062
2064
  if (approvalGate && (options.permissionMode || "default") === "auto") {
2063
2065
  // autoMode.decisions: user-configured riskLevel → allow/ask/deny
2064
2066
  // classifier (same wiring as headless-runner). Only wrap when settings
@@ -2082,6 +2084,7 @@ async function runAgentHeadlessStreamInWorkspace(
2082
2084
  }
2083
2085
  }
2084
2086
  if (approvalGate) {
2087
+ approvalGate.setAuthorizationConsumer?.(null);
2085
2088
  approvalGate.setSessionPolicy?.(sessionId, perm.sessionPolicy);
2086
2089
  approvalGate.setConfirmer?.(
2087
2090
  interactive && perm.allowInteractiveApprovals
@@ -2742,6 +2745,18 @@ async function runAgentHeadlessStreamInWorkspace(
2742
2745
  options.interactiveApprovals !== true
2743
2746
  ) {
2744
2747
  try {
2748
+ if (
2749
+ !approvalGate ||
2750
+ typeof approvalGate.setConfirmer !== "function" ||
2751
+ typeof approvalGate.setAuthorizationConsumer !== "function" ||
2752
+ typeof approvalGate.consumeAuthorization !== "function"
2753
+ ) {
2754
+ const error = new Error(
2755
+ "installed session-core cannot bind and consume durable remote approval",
2756
+ );
2757
+ error.code = "CC_REMOTE_APPROVAL_GATE_UNAVAILABLE";
2758
+ throw error;
2759
+ }
2745
2760
  const startRemoteApproval =
2746
2761
  deps.startHeadlessRemoteApproval ||
2747
2762
  (await import("../lib/remote-approval-bridge.js"))
@@ -2757,9 +2772,10 @@ async function runAgentHeadlessStreamInWorkspace(
2757
2772
  remote_session_id: _remoteApproval.pairing.remoteSessionId,
2758
2773
  expires_at: _remoteApproval.pairing.expiresAt,
2759
2774
  });
2760
- if (approvalGate && typeof approvalGate.setConfirmer === "function") {
2761
- approvalGate.setConfirmer(_remoteApproval.confirmer);
2762
- }
2775
+ approvalGate.setConfirmer(_remoteApproval.confirmer);
2776
+ approvalGate.setAuthorizationConsumer(
2777
+ _remoteApproval.consumeAuthorization,
2778
+ );
2763
2779
  } catch (err) {
2764
2780
  emit({
2765
2781
  type: "remote_control",
@@ -27,11 +27,13 @@ import {
27
27
  BACKGROUND_AGENT_KEEPER_ARMED,
28
28
  BACKGROUND_AGENT_KEEPER_CLEANUP_CONFIRM_TIMEOUT_MS,
29
29
  BACKGROUND_AGENT_KEEPER_HELLO,
30
+ BACKGROUND_AGENT_KEEPER_PERSIST_RETRY_TIMEOUT_MS,
30
31
  BACKGROUND_AGENT_KEEPER_PROTOCOL_VERSION,
31
32
  BACKGROUND_AGENT_KEEPER_READY,
32
33
  BACKGROUND_AGENT_KEEPER_RETIRE,
33
34
  BACKGROUND_AGENT_KEEPER_RETIRED,
34
35
  BACKGROUND_AGENT_KEEPER_STATE_LOCK_TIMEOUT_MS,
36
+ cleanupBackgroundAgentKeeperPipeDirectory,
35
37
  createBackgroundAgentKeeperMessage,
36
38
  normalizeBackgroundAgentKeeperHello,
37
39
  normalizeBackgroundAgentKeeperTurn,
@@ -40,6 +42,50 @@ import {
40
42
 
41
43
  const STARTUP_TIMEOUT_MS = 30_000;
42
44
  const HEARTBEAT_INTERVAL_MS = 1_000;
45
+ const KEEPER_PERSIST_RETRY_DELAY_MS = 50;
46
+
47
+ function delay(ms) {
48
+ return new Promise((resolvePromise) => setTimeout(resolvePromise, ms));
49
+ }
50
+
51
+ /**
52
+ * Retry a cleanup-critical state mutation after a worker is hard-killed.
53
+ *
54
+ * A worker can disappear while it owns the cross-process state lock. Windows
55
+ * may keep that terminated PID observable briefly, so the strict lock cannot
56
+ * immediately prove the owner dead and correctly fails closed. The keeper is
57
+ * the only remaining owner of the armed turn; it must wait for that transient
58
+ * fence instead of turning a lock exception into an unhandled rejection and
59
+ * exiting with the durable projection still `armed`.
60
+ */
61
+ export async function retryKeeperPersistence(operation, options = {}) {
62
+ const now = options.now || Date.now;
63
+ const sleep = options.sleep || delay;
64
+ const timeoutMs = Math.max(
65
+ 0,
66
+ Number(
67
+ options.timeoutMs ?? BACKGROUND_AGENT_KEEPER_PERSIST_RETRY_TIMEOUT_MS,
68
+ ),
69
+ );
70
+ const retryDelayMs = Math.max(
71
+ 1,
72
+ Number(options.retryDelayMs ?? KEEPER_PERSIST_RETRY_DELAY_MS),
73
+ );
74
+ const deadline = now() + timeoutMs;
75
+ let attempts = 0;
76
+ let error = null;
77
+ for (;;) {
78
+ attempts += 1;
79
+ try {
80
+ return { result: operation(), error: null, attempts };
81
+ } catch (cause) {
82
+ error = cause;
83
+ }
84
+ const remaining = deadline - now();
85
+ if (remaining <= 0) return { result: null, error, attempts };
86
+ await sleep(Math.min(retryDelayMs, remaining));
87
+ }
88
+ }
43
89
 
44
90
  function writeMessage(socket, message) {
45
91
  if (!socket || socket.destroyed) return false;
@@ -59,6 +105,60 @@ export function keeperWorkerIdentityAlive(
59
105
  return probe(Number(workerPid), Number(workerStartedAt));
60
106
  }
61
107
 
108
+ function reportKeeperPersistenceFailure(stage, error) {
109
+ try {
110
+ writeSync(
111
+ 2,
112
+ `[background-agent-keeper] ${stage} persistence failed: ${error?.stack || error?.message || String(error)}\n`,
113
+ );
114
+ } catch {
115
+ // The inherited diagnostic handle may already be closed.
116
+ }
117
+ }
118
+
119
+ export function runBackgroundAgentKeeperHeartbeat({
120
+ finishing,
121
+ workerSocket,
122
+ authenticatedHello,
123
+ authenticatedWorkerStartedAt,
124
+ armedTurn,
125
+ persistKeeper,
126
+ finishForWorkerDisconnect,
127
+ now = Date.now,
128
+ workerIdentityAlive = keeperWorkerIdentityAlive,
129
+ reportPersistenceFailure = reportKeeperPersistenceFailure,
130
+ }) {
131
+ if (finishing) return false;
132
+ if (
133
+ workerSocket &&
134
+ authenticatedHello &&
135
+ armedTurn &&
136
+ !workerIdentityAlive(
137
+ authenticatedHello.workerPid,
138
+ authenticatedWorkerStartedAt,
139
+ )
140
+ ) {
141
+ // A Windows named-pipe handle can remain open after its worker dies if a
142
+ // platform helper retained a duplicate. Socket EOF alone is therefore
143
+ // not a sufficient lifetime signal. The generation-bound worker PID and
144
+ // its durable launch anchor provide an independent identity fence.
145
+ finishForWorkerDisconnect();
146
+ workerSocket.destroy();
147
+ return false;
148
+ }
149
+ try {
150
+ persistKeeper({ keeperHeartbeatAt: now() });
151
+ return true;
152
+ } catch (error) {
153
+ try {
154
+ reportPersistenceFailure("heartbeat", error);
155
+ } catch {
156
+ // Diagnostics are best effort and must not escape the timer callback.
157
+ }
158
+ return false;
159
+ }
160
+ }
161
+
62
162
  function stateOwnsTurn(state, turn) {
63
163
  return Boolean(
64
164
  state &&
@@ -90,35 +190,77 @@ function persistKeeperTurn(job, turn, patch) {
90
190
  );
91
191
  }
92
192
 
193
+ export function stopBackgroundAgentKeeperTurnTrees(targets, options = {}) {
194
+ const platform = options.platform || process.platform;
195
+ const processAlive = options.isProcessAlive || isProcessAlive;
196
+ const processTreeExecutionAlive =
197
+ options.isProcessTreeExecutionAlive ||
198
+ isBackgroundProcessTreeExecutionAlive;
199
+ const stopTree = options.stopProcessTree || stopBackgroundAgentChildTree;
200
+ const failures = [];
201
+ let precedingTreeStopSucceeded = false;
202
+ for (const target of targets) {
203
+ // On Windows the wrapper's taskkill /T also retires its runtime child.
204
+ // Avoid starting a second bounded taskkill for a target the first tree
205
+ // operation already proved absent; under 20-way churn that redundant
206
+ // command can consume the keeper's entire cleanup SLO. Never skip the
207
+ // first root merely because its leader exited: POSIX descendants can keep
208
+ // executing in the leader's process group and still require a group kill.
209
+ if (precedingTreeStopSucceeded && !processAlive(target.pid)) continue;
210
+ try {
211
+ stopTree(target.pid, {
212
+ signal: "SIGKILL",
213
+ expectedStartedAt: target.startedAt,
214
+ processGroup: target.processGroup,
215
+ strictIdentity: true,
216
+ allowDirectFallback: false,
217
+ });
218
+ precedingTreeStopSucceeded = true;
219
+ } catch (error) {
220
+ // A detached POSIX group can keep executing after its leader exits. Do
221
+ // not discard the strict stop failure from a leader-only liveness probe;
222
+ // the group-aware residual check preserves the actionable identity
223
+ // reason while descendants remain executable.
224
+ const leaderAlive = processAlive(target.pid);
225
+ const residualAlive =
226
+ leaderAlive ||
227
+ (platform !== "win32" &&
228
+ target.processGroup === true &&
229
+ processTreeExecutionAlive(target.pid, target.startedAt));
230
+ if (residualAlive) {
231
+ failures.push(error?.message || String(error));
232
+ }
233
+ }
234
+ }
235
+ return failures;
236
+ }
237
+
93
238
  async function cleanupTurn(job, turn, reason) {
94
239
  const requestedAt = Date.now();
95
- persistKeeperTurn(job, turn, {
96
- turnKeeperStatus: "cleanup-requested",
97
- turnKeeperCleanupReason: reason,
98
- turnKeeperCleanupRequestedAt: requestedAt,
99
- });
240
+ const requestedPersistence = await retryKeeperPersistence(() =>
241
+ persistKeeperTurn(job, turn, {
242
+ turnKeeperStatus: "cleanup-requested",
243
+ turnKeeperCleanupReason: reason,
244
+ turnKeeperCleanupRequestedAt: requestedAt,
245
+ }),
246
+ );
100
247
 
101
248
  const targets = [
102
- { pid: turn.agentPid, startedAt: turn.agentStartedAt },
249
+ {
250
+ pid: turn.agentPid,
251
+ startedAt: turn.agentStartedAt,
252
+ processGroup: true,
253
+ },
103
254
  {
104
255
  pid: turn.agentRuntimePid,
105
256
  startedAt: turn.agentRuntimeStartedAt,
257
+ processGroup: false,
106
258
  },
107
259
  ].filter(
108
260
  (target, index, values) =>
109
261
  values.findIndex((candidate) => candidate.pid === target.pid) === index,
110
262
  );
111
- const failures = [];
112
- for (const target of targets) {
113
- try {
114
- stopBackgroundAgentChildTree(target.pid, { signal: "SIGKILL" });
115
- } catch (error) {
116
- // A preceding wrapper/group signal may already have retired this root.
117
- if (isProcessAlive(target.pid)) {
118
- failures.push(error?.message || String(error));
119
- }
120
- }
121
- }
263
+ const failures = stopBackgroundAgentKeeperTurnTrees(targets);
122
264
 
123
265
  const deadline =
124
266
  Date.now() + BACKGROUND_AGENT_KEEPER_CLEANUP_CONFIRM_TIMEOUT_MS;
@@ -132,19 +274,37 @@ async function cleanupTurn(job, turn, reason) {
132
274
  );
133
275
  }
134
276
  const confirmed = alive.length === 0 && failures.length === 0;
135
- persistKeeperTurn(job, turn, {
136
- turnKeeperStatus: confirmed ? "retired" : "cleanup-unconfirmed",
137
- turnKeeperCleanupReason: reason,
138
- turnKeeperCleanupRequestedAt: requestedAt,
139
- turnKeeperCleanupConfirmedAt: confirmed ? Date.now() : null,
140
- turnKeeperCleanupError:
141
- failures.length > 0
142
- ? failures.join("; ").slice(0, 1_000)
143
- : alive.length > 0
144
- ? `process tree still executable: ${alive.map(({ pid }) => pid).join(",")}`
145
- : null,
277
+ let finalPersistenceAttempts = 0;
278
+ const finalPersistence = await retryKeeperPersistence(() => {
279
+ finalPersistenceAttempts += 1;
280
+ return persistKeeperTurn(job, turn, {
281
+ turnKeeperStatus: confirmed ? "retired" : "cleanup-unconfirmed",
282
+ turnKeeperCleanupReason: reason,
283
+ turnKeeperCleanupRequestedAt: requestedAt,
284
+ turnKeeperCleanupConfirmedAt: confirmed ? Date.now() : null,
285
+ turnKeeperCleanupError:
286
+ failures.length > 0
287
+ ? failures.join("; ").slice(0, 1_000)
288
+ : alive.length > 0
289
+ ? `process tree still executable: ${alive.map(({ pid }) => pid).join(",")}`
290
+ : null,
291
+ turnKeeperPersistenceRetries:
292
+ Math.max(0, requestedPersistence.attempts - 1) +
293
+ Math.max(0, finalPersistenceAttempts - 1),
294
+ });
146
295
  });
147
- return { confirmed, failures, alive };
296
+ const persistenceError = finalPersistence.error
297
+ ? finalPersistence.error?.message || String(finalPersistence.error)
298
+ : finalPersistence.result?.applied === false
299
+ ? "keeper cleanup state no longer owned"
300
+ : null;
301
+ if (persistenceError) failures.push(persistenceError);
302
+ return {
303
+ confirmed: confirmed && !persistenceError,
304
+ failures,
305
+ alive,
306
+ persistenceError,
307
+ };
148
308
  }
149
309
 
150
310
  export async function runBackgroundAgentKeeper(jobFile, options = {}) {
@@ -206,17 +366,50 @@ export async function runBackgroundAgentKeeper(jobFile, options = {}) {
206
366
  if (finishing) return false;
207
367
  finishing = true;
208
368
  const active = armedTurn;
209
- void (
210
- active
211
- ? cleanupTurn(job, active, "worker-disconnected")
212
- : Promise.resolve({ confirmed: true })
213
- ).finally(() => {
214
- persistKeeper({
215
- keeperStatus: active ? "worker-disconnected" : "closed",
216
- keeperEndedAt: Date.now(),
217
- });
369
+ void (async () => {
370
+ let cleanup;
371
+ try {
372
+ cleanup = active
373
+ ? await cleanupTurn(job, active, "worker-disconnected")
374
+ : { confirmed: true, failures: [], alive: [] };
375
+ } catch (error) {
376
+ cleanup = {
377
+ confirmed: false,
378
+ failures: [error?.message || String(error)],
379
+ alive: [],
380
+ };
381
+ }
382
+ // Closing the keeper must never be skipped because one final metadata
383
+ // update throws. Retry the critical status write, retain the failure in
384
+ // the log, and always settle the server promise without an unhandled
385
+ // rejection.
386
+ const persisted = await retryKeeperPersistence(() =>
387
+ persistKeeper({
388
+ keeperStatus: active
389
+ ? cleanup.confirmed
390
+ ? "worker-disconnected"
391
+ : "cleanup-unconfirmed"
392
+ : "closed",
393
+ keeperEndedAt: Date.now(),
394
+ ...(cleanup.failures.length > 0
395
+ ? {
396
+ keeperError: cleanup.failures.join("; ").slice(0, 1_000),
397
+ }
398
+ : {}),
399
+ }),
400
+ );
401
+ if (persisted.error) {
402
+ try {
403
+ writeSync(
404
+ 2,
405
+ `[background-agent-keeper] final state persistence failed: ${persisted.error?.stack || persisted.error?.message || String(persisted.error)}\n`,
406
+ );
407
+ } catch {
408
+ // The inherited diagnostic handle may already be closed.
409
+ }
410
+ }
218
411
  server.close(() => finishResolve({ status: "closed" }));
219
- });
412
+ })();
220
413
  return true;
221
414
  };
222
415
 
@@ -413,26 +606,19 @@ export async function runBackgroundAgentKeeper(jobFile, options = {}) {
413
606
  socket.once("error", disconnected);
414
607
  });
415
608
 
416
- const heartbeat = setInterval(() => {
417
- if (
418
- workerSocket &&
419
- authenticatedHello &&
420
- armedTurn &&
421
- !keeperWorkerIdentityAlive(
422
- authenticatedHello.workerPid,
609
+ const heartbeat = setInterval(
610
+ () =>
611
+ runBackgroundAgentKeeperHeartbeat({
612
+ finishing,
613
+ workerSocket,
614
+ authenticatedHello,
423
615
  authenticatedWorkerStartedAt,
424
- )
425
- ) {
426
- // A Windows named-pipe handle can remain open after its worker dies if a
427
- // platform helper retained a duplicate. Socket EOF alone is therefore
428
- // not a sufficient lifetime signal. The generation-bound worker PID and
429
- // its durable launch anchor provide an independent identity fence.
430
- finishForWorkerDisconnect();
431
- workerSocket.destroy();
432
- return;
433
- }
434
- persistKeeper({ keeperHeartbeatAt: Date.now() });
435
- }, HEARTBEAT_INTERVAL_MS);
616
+ armedTurn,
617
+ persistKeeper,
618
+ finishForWorkerDisconnect,
619
+ }),
620
+ HEARTBEAT_INTERVAL_MS,
621
+ );
436
622
  heartbeat.unref?.();
437
623
  const startupTimer = setTimeout(
438
624
  () => {
@@ -464,6 +650,11 @@ export async function runBackgroundAgentKeeper(jobFile, options = {}) {
464
650
  } catch {
465
651
  // Already removed or never materialized.
466
652
  }
653
+ try {
654
+ cleanupBackgroundAgentKeeperPipeDirectory(job.pipePath);
655
+ } catch {
656
+ // Another keeper can still own a sibling socket in the shared namespace.
657
+ }
467
658
  }
468
659
  return {
469
660
  ...result,
@@ -1472,6 +1472,13 @@ async function main() {
1472
1472
  },
1473
1473
  onStop: () => {
1474
1474
  if (!child) return;
1475
+ // An explicit stop owns this turn's termination. Detach the bootstrap
1476
+ // release callback before killing the child so a late IPC EPIPE cannot
1477
+ // reclassify the intentional stop as a post-spawn launch failure. The
1478
+ // child termination settlement remains attached and advances any
1479
+ // queued follow-up turn after the tree is confirmed closed.
1480
+ detachTurnBootstrapHandler?.();
1481
+ detachTurnBootstrapHandler = null;
1475
1482
  // The agent child is detached into its own group on POSIX; Windows
1476
1483
  // requires brokered taskkill /T /F so tool grandchildren cannot
1477
1484
  // survive an attached-session stop.
@@ -14,6 +14,7 @@ import {
14
14
  BACKGROUND_TURN_BOOTSTRAP_RELEASE,
15
15
  backgroundTurnBootstrapBindingFromEnvironment,
16
16
  clearBackgroundTurnBootstrapEnvironment,
17
+ containReleasedBackgroundTurnDisconnect,
17
18
  createBackgroundTurnBootstrapMessage,
18
19
  matchesBackgroundTurnBootstrapMessage,
19
20
  removeBackgroundTurnBootstrapImport,
@@ -31,12 +32,16 @@ function releaseTimeoutMs() {
31
32
  return Math.max(10, Math.min(TEST_RELEASE_TIMEOUT_MAX_MS, requested));
32
33
  }
33
34
 
34
- function failClosed(message) {
35
+ function reportFailure(message) {
35
36
  try {
36
37
  writeSync(2, `[background-turn-bootstrap] ${message}\n`);
37
38
  } catch {
38
39
  // The inherited diagnostic handle may already be closed.
39
40
  }
41
+ }
42
+
43
+ function failClosed(message) {
44
+ reportFailure(message);
40
45
  process.exit(1);
41
46
  }
42
47
 
@@ -68,6 +73,28 @@ await new Promise((resolve) => {
68
73
  failClosed(message);
69
74
  };
70
75
 
76
+ const containDisconnectOnce = (message) => {
77
+ if (failed) return;
78
+ failed = true;
79
+ if (timer) clearTimeout(timer);
80
+ if (readyTimer) clearInterval(readyTimer);
81
+ // Emit the original diagnostic before SIGSTOP freezes this process and
82
+ // every descendant in its detached POSIX group. If the stop fails (or a
83
+ // later SIGCONT lets the helper's fail-safe SIGKILL fail), retain the
84
+ // historical single-process fail-closed exit.
85
+ reportFailure(message);
86
+ if (
87
+ containReleasedBackgroundTurnDisconnect({
88
+ released,
89
+ currentPid: binding.pid,
90
+ onResumedKillFailure: () => process.exit(1),
91
+ })
92
+ ) {
93
+ return;
94
+ }
95
+ process.exit(1);
96
+ };
97
+
71
98
  const onMessage = (message) => {
72
99
  if (message?.type !== BACKGROUND_TURN_BOOTSTRAP_RELEASE) return;
73
100
  if (released) {
@@ -109,7 +136,7 @@ await new Promise((resolve) => {
109
136
  // supervising worker remains fail-closed even after the real CLI starts.
110
137
  process.on("message", onMessage);
111
138
  process.once("disconnect", () =>
112
- failOnce("worker IPC disconnected before turn completion"),
139
+ containDisconnectOnce("worker IPC disconnected before turn completion"),
113
140
  );
114
141
  timer = setTimeout(
115
142
  () => failOnce("timed out waiting for durable turn release"),
@@ -1 +0,0 @@
1
- import{I as n,J as g,c as l,K as r,N as u,P as h,U as o,R as t,Q as b}from"./vendor-Bn1JzzAn.js";import{_ as v,b as m}from"./index-BQ3DXBtD.js";import{a8 as M,M as y}from"./icons-DG5UD-6K.js";const B={__name:"MobileProjects",setup(p,{expose:i}){i();const c=u(),{t:e}=m();function a(){c.push("/projects")}function _(){c.push("/mobile-bridge")}const s={router:c,t:e,goToProjects:a,goToMobileBridge:_,get useRouter(){return u},get useI18n(){return m},get MobileOutlined(){return y},get FolderOutlined(){return M}};return Object.defineProperty(s,"__isScriptSetup",{enumerable:!1,value:!0}),s}},x={class:"mobile-projects-placeholder"},O={class:"title"},k={class:"subtitle"},w={class:"explainer"};function T(p,i,c,e,a,_){const s=n("a-alert"),d=n("a-button"),f=n("a-space"),P=n("a-empty"),j=n("a-card");return h(),g("div",x,[l(j,null,{default:r(()=>[l(P,{description:!1},{image:r(()=>[l(e.MobileOutlined,{style:{fontSize:"64px",color:"#bfbfbf"}})]),default:r(()=>[o("h2",O,t(e.t("mobileProjects.title")),1),o("p",k,t(e.t("mobileProjects.subtitle")),1),l(s,{message:e.t("mobileProjects.v02Banner"),type:"info","show-icon":"",class:"banner"},null,8,["message"]),o("div",w,[o("h3",null,t(e.t("mobileProjects.currentDirection")),1),o("p",null,t(e.t("mobileProjects.currentDirectionBody")),1),o("ul",null,[o("li",null,t(e.t("mobileProjects.stepPhone")),1),o("li",null,t(e.t("mobileProjects.stepTap")),1),o("li",null,t(e.t("mobileProjects.stepPull")),1)]),o("h3",null,t(e.t("mobileProjects.reverseDirection")),1),o("p",null,t(e.t("mobileProjects.reverseDirectionBody")),1)]),l(f,{class:"actions"},{default:r(()=>[l(d,{type:"primary",onClick:e.goToProjects},{default:r(()=>[l(e.FolderOutlined),b(" "+t(e.t("mobileProjects.viewLocalProjects")),1)]),_:1}),l(d,{onClick:e.goToMobileBridge},{default:r(()=>[l(e.MobileOutlined),b(" "+t(e.t("mobileProjects.checkBridge")),1)]),_:1})]),_:1})]),_:1})]),_:1})])}const N=v(B,[["render",T],["__scopeId","data-v-9262cc45"],["__file","/tmp/cc-web-panel-J6U3Mt/repo/packages/web-panel/src/views/MobileProjects.vue"]]);export{N as default};