chainlesschain 0.162.154 → 0.162.155

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 (198) hide show
  1. package/README.md +1 -1
  2. package/package.json +1 -1
  3. package/src/assets/web-panel/.build-hash +1 -1
  4. package/src/assets/web-panel/assets/{AIOps-CIpoXyVI.js → AIOps-Cth_zZeA.js} +1 -1
  5. package/src/assets/web-panel/assets/{ActionButton-Ch_QXIPk.js → ActionButton-zfwA3Oxn.js} +1 -1
  6. package/src/assets/web-panel/assets/{Analytics-CvaBYShv.js → Analytics-FeKc4aU2.js} +2 -2
  7. package/src/assets/web-panel/assets/AppLayout-C6WIrDZk.js +9 -0
  8. package/src/assets/web-panel/assets/{Audit-Bng8eHCW.js → Audit-C2gRq5Qi.js} +1 -1
  9. package/src/assets/web-panel/assets/BackgroundAgents-Dq2340Kl.css +1 -0
  10. package/src/assets/web-panel/assets/BackgroundAgents-TeTIFiW-.js +1 -0
  11. package/src/assets/web-panel/assets/{Backup-DvBs8-lm.js → Backup-CLJk3k6i.js} +1 -1
  12. package/src/assets/web-panel/assets/{BaseInput-DKwajm0g.js → BaseInput-5ms537NY.js} +1 -1
  13. package/src/assets/web-panel/assets/{Chat-BoGpZ3uO.js → Chat-4Nr5U9PJ.js} +5 -5
  14. package/src/assets/web-panel/assets/ChatBubbleRenderer-BFEnA8LN.js +1 -0
  15. package/src/assets/web-panel/assets/{Checkbox-BMJEmxFQ.js → Checkbox-B2Bca5ev.js} +1 -1
  16. package/src/assets/web-panel/assets/{Codegen-Bsoag5CV.js → Codegen-D6Uysdbv.js} +1 -1
  17. package/src/assets/web-panel/assets/{Col-CDjH9-sp.js → Col-B3XQ8xc7.js} +1 -1
  18. package/src/assets/web-panel/assets/{Community-Cg2QHPQ6.js → Community-CqwKtoPz.js} +1 -1
  19. package/src/assets/web-panel/assets/{Compact-BU2BaqP-.js → Compact-BZZINTus.js} +1 -1
  20. package/src/assets/web-panel/assets/{Compliance-D6ZYMCqZ.js → Compliance-Dr3BXkWA.js} +1 -1
  21. package/src/assets/web-panel/assets/{Cowork-DaZ8VW-I.js → Cowork-rx_x9Wqc.js} +2 -2
  22. package/src/assets/web-panel/assets/{Cron-DQY1RH9v.js → Cron-BpZr7rHc.js} +2 -2
  23. package/src/assets/web-panel/assets/{Crosschain-BVR28vk9.js → Crosschain-CeV5EYVg.js} +1 -1
  24. package/src/assets/web-panel/assets/{DID-N44KvTP-.js → DID-fe2Qaowz.js} +2 -2
  25. package/src/assets/web-panel/assets/{Dashboard-Bjhlx6wS.js → Dashboard-Fh_EdWVa.js} +2 -2
  26. package/src/assets/web-panel/assets/{Dropdown-CpLYxpy1.js → Dropdown-N1PIwWYf.js} +1 -1
  27. package/src/assets/web-panel/assets/{EmailListRenderer-srEITSPI.js → EmailListRenderer-B6vK93Ed.js} +1 -1
  28. package/src/assets/web-panel/assets/{FamilyGuardDashboard-juOC_fX2.js → FamilyGuardDashboard-e3iUZhAg.js} +1 -1
  29. package/src/assets/web-panel/assets/{Federation-nRaQtaXW.js → Federation-uInc9YcN.js} +1 -1
  30. package/src/assets/web-panel/assets/{FormItemContext-DflhTj09.js → FormItemContext-DmQZSX0z.js} +1 -1
  31. package/src/assets/web-panel/assets/{GenericCardRenderer-DulI_3NI.js → GenericCardRenderer-CV_Z8OAo.js} +1 -1
  32. package/src/assets/web-panel/assets/{Git-BxOPefXn.js → Git-jXAhNN8d.js} +2 -2
  33. package/src/assets/web-panel/assets/{Governance-C6G_Wnwi.js → Governance-Blw1esBB.js} +1 -1
  34. package/src/assets/web-panel/assets/{Inference-BnFYa2BF.js → Inference-CoK8Ia0Z.js} +1 -1
  35. package/src/assets/web-panel/assets/{KnowledgeGraph-TqQLEabm.js → KnowledgeGraph-5xrglMTK.js} +1 -1
  36. package/src/assets/web-panel/assets/{Logs-BH4AVh7s.js → Logs-BGIG9798.js} +2 -2
  37. package/src/assets/web-panel/assets/{Marketplace-DoWLTaEE.js → Marketplace-DFqhJMuY.js} +1 -1
  38. package/src/assets/web-panel/assets/{McpTools-D6yC7OG_.js → McpTools-7U8tEhwK.js} +5 -5
  39. package/src/assets/web-panel/assets/{Memory-DLCeHj21.js → Memory-BrvboPLE.js} +2 -2
  40. package/src/assets/web-panel/assets/{MobileBridge-BZBE0GRl.js → MobileBridge-nzcd7d1H.js} +1 -1
  41. package/src/assets/web-panel/assets/{MobileProjects-DJA15G1J.js → MobileProjects-DETVNKa7.js} +1 -1
  42. package/src/assets/web-panel/assets/{Mtc-CyTKJDHn.js → Mtc-SZcCYec3.js} +4 -4
  43. package/src/assets/web-panel/assets/{MtcAudit-DZKjgEVc.js → MtcAudit-Cx_d3N17.js} +5 -5
  44. package/src/assets/web-panel/assets/{Multisig-2rMPNhSO.js → Multisig-DtcmSUo-.js} +3 -3
  45. package/src/assets/web-panel/assets/{NLProgramming-VeMk4Fx-.js → NLProgramming-Dgx2QHl-.js} +1 -1
  46. package/src/assets/web-panel/assets/{Notes-jr2GHMhh.js → Notes-oHTGEHoJ.js} +3 -3
  47. package/src/assets/web-panel/assets/{NotificationSettings-WN9VZyFb.js → NotificationSettings-qKD-IlOl.js} +1 -1
  48. package/src/assets/web-panel/assets/{OrderTableRenderer-CjNO-e9T.js → OrderTableRenderer-CiU4Sy1J.js} +1 -1
  49. package/src/assets/web-panel/assets/{Organization-DdjVHuzT.js → Organization-BBNXnltP.js} +4 -4
  50. package/src/assets/web-panel/assets/{Overflow-l8vBsGGq.js → Overflow-Df2fDbvT.js} +1 -1
  51. package/src/assets/web-panel/assets/{OverrideContext-BlgL9440.js → OverrideContext-C0w0P4A3.js} +1 -1
  52. package/src/assets/web-panel/assets/{P2P-CVQDHHwq.js → P2P-DUHj6zjO.js} +2 -2
  53. package/src/assets/web-panel/assets/{PdhVaultBrowser-CTsNh8qE.js → PdhVaultBrowser-B2nWgdoV.js} +4 -4
  54. package/src/assets/web-panel/assets/{Permissions-DLW87uyK.js → Permissions-CiW0afny.js} +3 -3
  55. package/src/assets/web-panel/assets/{PersonalDataHub-N8OvASTx.js → PersonalDataHub-cc1mHU_D.js} +2 -2
  56. package/src/assets/web-panel/assets/{Pipeline-BPZmdlz2.js → Pipeline-B93zTapU.js} +1 -1
  57. package/src/assets/web-panel/assets/{Privacy-MFZw_GNh.js → Privacy-Db1jXWfd.js} +1 -1
  58. package/src/assets/web-panel/assets/{ProjectInit-zW0pEsfU.js → ProjectInit-DFoZ1h9U.js} +2 -2
  59. package/src/assets/web-panel/assets/{ProjectSettings-CppVHr_o.js → ProjectSettings-BkkUnmYs.js} +2 -2
  60. package/src/assets/web-panel/assets/{Projects-BTglkJO2.js → Projects-Xa779XLC.js} +1 -1
  61. package/src/assets/web-panel/assets/{Providers-CPHZ6JKT.js → Providers-DOF01IEN.js} +1 -1
  62. package/src/assets/web-panel/assets/{QrScannerModal-DO4klNZ2.js → QrScannerModal-TohRsKd0.js} +1 -1
  63. package/src/assets/web-panel/assets/{QuickAsk-B37TTqUa.js → QuickAsk-DjLcr1ZU.js} +1 -1
  64. package/src/assets/web-panel/assets/{Recommend-n7KIfK7y.js → Recommend--Ism6bMx.js} +1 -1
  65. package/src/assets/web-panel/assets/{RemoteSession-SjVoS9mm.js → RemoteSession-B8CL3YHM.js} +2 -2
  66. package/src/assets/web-panel/assets/{Reputation-Dt6RTeHi.js → Reputation-CaRxB3Ep.js} +1 -1
  67. package/src/assets/web-panel/assets/{Row-Dty6jz5V.js → Row-BioxZBBH.js} +1 -1
  68. package/src/assets/web-panel/assets/{RssFeed-DDLhqw5j.js → RssFeed-B2LipwH3.js} +3 -3
  69. package/src/assets/web-panel/assets/{Search-DrYbXi-W.js → Search-BpjLBV8u.js} +1 -1
  70. package/src/assets/web-panel/assets/{Security-Fc9Z6fbB.js → Security-TibHuJWP.js} +3 -3
  71. package/src/assets/web-panel/assets/{Services-BAhPgjSM.js → Services-DtkuWseW.js} +2 -2
  72. package/src/assets/web-panel/assets/{Skeleton-8JF7EPfE.js → Skeleton-BIcPQae6.js} +1 -1
  73. package/src/assets/web-panel/assets/Skills-DoTWIQOX.js +1 -0
  74. package/src/assets/web-panel/assets/{Sla-CSvjcATc.js → Sla-uUSU185m.js} +1 -1
  75. package/src/assets/web-panel/assets/{SpeechSettings-Demx7Gnx.js → SpeechSettings-B7Bgc9mk.js} +1 -1
  76. package/src/assets/web-panel/assets/{SyncSettings-CKLnRWJr.js → SyncSettings-K-XrJ2s9.js} +2 -2
  77. package/src/assets/web-panel/assets/{Tasks-mhITRxjv.js → Tasks-B3hEXNcI.js} +1 -1
  78. package/src/assets/web-panel/assets/{Templates-AjQI5rxD.js → Templates-CaFnyGDK.js} +1 -1
  79. package/src/assets/web-panel/assets/{Tenant-dbtEh1yR.js → Tenant-C2X0PtEC.js} +1 -1
  80. package/src/assets/web-panel/assets/{Terminal-DX-9jjfk.js → Terminal-CKU5yVsT.js} +2 -2
  81. package/src/assets/web-panel/assets/{TimelineRenderer-C11EDZRt.js → TimelineRenderer-8trZeYDC.js} +1 -1
  82. package/src/assets/web-panel/assets/{Tokens-GiA9fiZn.js → Tokens-BUEEe9US.js} +1 -1
  83. package/src/assets/web-panel/assets/{Trigger-BPcnclJn.js → Trigger-COU97I7Y.js} +1 -1
  84. package/src/assets/web-panel/assets/{Trust-C-DVY8nq.js → Trust-DNWQChaP.js} +1 -1
  85. package/src/assets/web-panel/assets/{UkeySign-DxodSwME.js → UkeySign-DWX_VtCp.js} +1 -1
  86. package/src/assets/web-panel/assets/{VideoEditing-D_WV0O8O.js → VideoEditing-D9eIDhsp.js} +1 -1
  87. package/src/assets/web-panel/assets/{Wallet-DSeNlVSU.js → Wallet-BOnCCgIL.js} +4 -4
  88. package/src/assets/web-panel/assets/{WebAuthn-Bwuuvpz1.js → WebAuthn-DN4dD-W8.js} +4 -4
  89. package/src/assets/web-panel/assets/{WorkflowEditor-8M96EU1N.js → WorkflowEditor-ClvDb_jX.js} +1 -1
  90. package/src/assets/web-panel/assets/{chat-D5S6SmLm.js → chat-DIxQ_Eeb.js} +1 -1
  91. package/src/assets/web-panel/assets/{collapseMotion-DVfhbsdP.js → collapseMotion-CqM1QOwM.js} +1 -1
  92. package/src/assets/web-panel/assets/{colors-5fbnGkAP.js → colors-BOX3WKcP.js} +1 -1
  93. package/src/assets/web-panel/assets/{compact-item-DaSuuMlW.js → compact-item-Bny82VDi.js} +1 -1
  94. package/src/assets/web-panel/assets/{createContext-BNOHcXAF.js → createContext-Cnoy3enS.js} +1 -1
  95. package/src/assets/web-panel/assets/devWarning-Dr9dzlQ5.js +1 -0
  96. package/src/assets/web-panel/assets/{echarts-CL0F2SnX.js → echarts-C61Vn3mP.js} +1 -1
  97. package/src/assets/web-panel/assets/{hasIn-DONwZUra.js → hasIn-BJ7fkx3F.js} +1 -1
  98. package/src/assets/web-panel/assets/{icons-BsDmGpcT.js → icons-DEmcnxBf.js} +1 -1
  99. package/src/assets/web-panel/assets/{index-B_yDD9Lm.js → index--CcGiwAI.js} +1 -1
  100. package/src/assets/web-panel/assets/{index-5_fj5gVd.js → index--Xb2lq6E.js} +1 -1
  101. package/src/assets/web-panel/assets/{index-uR5KTNZA.js → index-2N9Y1cLx.js} +1 -1
  102. package/src/assets/web-panel/assets/{index-MOgO5Yvx.js → index-7GLXvG8p.js} +1 -1
  103. package/src/assets/web-panel/assets/{index-CLXf-8ek.js → index-B1-h2QrC.js} +1 -1
  104. package/src/assets/web-panel/assets/{index-D7gs_3Ck.js → index-B6WoW8DJ.js} +1 -1
  105. package/src/assets/web-panel/assets/{index-CztESwB4.js → index-B7Fah_BX.js} +1 -1
  106. package/src/assets/web-panel/assets/index-B92M3t6B.js +1 -0
  107. package/src/assets/web-panel/assets/{index-BEO5f7Vf.js → index-BDkN6YOI.js} +1 -1
  108. package/src/assets/web-panel/assets/{index-_6S41ikV.js → index-BJ8-oLHI.js} +1 -1
  109. package/src/assets/web-panel/assets/{index-CrkCFohi.js → index-BM0mQhVv.js} +1 -1
  110. package/src/assets/web-panel/assets/{index-BrAJSu-v.js → index-BP7HFsAK.js} +1 -1
  111. package/src/assets/web-panel/assets/{index-BcqOK7Lv.js → index-BWPey0Uf.js} +1 -1
  112. package/src/assets/web-panel/assets/{index-BLzMuz38.js → index-BbZynivw.js} +1 -1
  113. package/src/assets/web-panel/assets/{index-DbdQBn4n.js → index-BcmXbIwv.js} +1 -1
  114. package/src/assets/web-panel/assets/{index-KwV9g_cf.js → index-Bk5yST66.js} +1 -1
  115. package/src/assets/web-panel/assets/{index-ClGqO-2X.js → index-Bvheiep2.js} +1 -1
  116. package/src/assets/web-panel/assets/{index-Dd7xwGcD.js → index-C--PPqwi.js} +1 -1
  117. package/src/assets/web-panel/assets/{index-DP2-3A_j.js → index-C1nHEOFE.js} +1 -1
  118. package/src/assets/web-panel/assets/{index-CAYmc5Nh.js → index-CEPTiBWy.js} +1 -1
  119. package/src/assets/web-panel/assets/{index-LRIt-AFl.js → index-CEq5Vh0i.js} +1 -1
  120. package/src/assets/web-panel/assets/{index-Dm5A-MN5.js → index-CN6emwbq.js} +1 -1
  121. package/src/assets/web-panel/assets/{index-Cf2OCa8q.js → index-CQ-xLCVb.js} +1 -1
  122. package/src/assets/web-panel/assets/{index-DHx2lhHW.js → index-CW6n-y1L.js} +1 -1
  123. package/src/assets/web-panel/assets/{index-DM3Ug61e.js → index-CZ6CHiCQ.js} +1 -1
  124. package/src/assets/web-panel/assets/{index-BJp4kRjw.js → index-CZCTan8Q.js} +1 -1
  125. package/src/assets/web-panel/assets/{index-PsMErAB1.js → index-CnjtAKdw.js} +1 -1
  126. package/src/assets/web-panel/assets/{index-CEzd5m5a.js → index-D2qNdrNW.js} +1 -1
  127. package/src/assets/web-panel/assets/{index-B4L3gsNA.js → index-D9QEc7Tq.js} +1 -1
  128. package/src/assets/web-panel/assets/{index-BcWMZqb4.js → index-DBhkJqVF.js} +1 -1
  129. package/src/assets/web-panel/assets/{index-CSfYJqXt.js → index-DSRIjAWO.js} +1 -1
  130. package/src/assets/web-panel/assets/{index-CmUOjyL6.js → index-DxX8hhmL.js} +1 -1
  131. package/src/assets/web-panel/assets/{index-RkInlwV0.js → index-Kio1ekYg.js} +1 -1
  132. package/src/assets/web-panel/assets/{index-CTZeOnzO.js → index-OaNFXV8N.js} +1 -1
  133. package/src/assets/web-panel/assets/{index-BK2N5yVW.js → index-Ty4w18mR.js} +4 -4
  134. package/src/assets/web-panel/assets/{index-Cn5LtyIH.js → index-ceH9DsAi.js} +1 -1
  135. package/src/assets/web-panel/assets/index-d2UcGpK-.js +1 -0
  136. package/src/assets/web-panel/assets/{index-xo2f4DZR.js → index-qOBKrw-g.js} +1 -1
  137. package/src/assets/web-panel/assets/{index-vYtprlNs.js → index-rn6xd12s.js} +1 -1
  138. package/src/assets/web-panel/assets/{initDefaultProps-CxwLynp9.js → initDefaultProps-BmxMBuUZ.js} +1 -1
  139. package/src/assets/web-panel/assets/{motion-XbPvGJbW.js → motion-DnXNwo2k.js} +1 -1
  140. package/src/assets/web-panel/assets/{move-D1u5pcgS.js → move-WnIJwmZN.js} +1 -1
  141. package/src/assets/web-panel/assets/{mtc-parser-BDxhTBMH.js → mtc-parser-DAto-c2w.js} +1 -1
  142. package/src/assets/web-panel/assets/{omit-EtIFGg5k.js → omit-BtvW9W0H.js} +1 -1
  143. package/src/assets/web-panel/assets/{pickAttrs-D33sWb0l.js → pickAttrs-DFqI9-_C.js} +1 -1
  144. package/src/assets/web-panel/assets/{placementArrow-BwFRzaGo.js → placementArrow-Dax6wOK3.js} +1 -1
  145. package/src/assets/web-panel/assets/{responsiveObserve-CBk2WqvQ.js → responsiveObserve-BUFWWC83.js} +1 -1
  146. package/src/assets/web-panel/assets/{slide-B52HtrKn.js → slide-DyARyuSz.js} +1 -1
  147. package/src/assets/web-panel/assets/{statusUtils-D4qxqWnH.js → statusUtils-BZSDugpi.js} +1 -1
  148. package/src/assets/web-panel/assets/{styleChecker-BxaCFlOL.js → styleChecker-B7o_K-4R.js} +1 -1
  149. package/src/assets/web-panel/assets/useFlexGapSupport-Dg7Bg8VG.js +1 -0
  150. package/src/assets/web-panel/assets/{useFs-C6VAU1_6.js → useFs-DQYXo5Rk.js} +1 -1
  151. package/src/assets/web-panel/assets/{useMergedState-Dc9sO2d7.js → useMergedState-DXbJa_zE.js} +1 -1
  152. package/src/assets/web-panel/assets/{usePersonalDataHub-rEG6oXG8.js → usePersonalDataHub-E8NBAnkq.js} +1 -1
  153. package/src/assets/web-panel/assets/{useRefs-CAeFRzPf.js → useRefs-BrCfMLMc.js} +1 -1
  154. package/src/assets/web-panel/assets/{useState-LwExUYDO.js → useState-CnxtbLoC.js} +1 -1
  155. package/src/assets/web-panel/assets/{vendor-BMxUs6UX.js → vendor-Bn1JzzAn.js} +1 -1
  156. package/src/assets/web-panel/assets/{vnode-DOTafkEi.js → vnode-DD71_w1b.js} +1 -1
  157. package/src/assets/web-panel/assets/{zoom-BuwwN5K2.js → zoom-zc9EMd3q.js} +1 -1
  158. package/src/assets/web-panel/index.html +3 -3
  159. package/src/command-manifest.json +36 -1
  160. package/src/commands/agent.js +23 -2
  161. package/src/commands/agents.js +62 -1
  162. package/src/commands/auto-mode.js +87 -0
  163. package/src/commands/background-session.js +499 -0
  164. package/src/commands/command.js +4 -1
  165. package/src/commands/permissions.js +49 -0
  166. package/src/commands/remote-control.js +407 -0
  167. package/src/data/changelog.json +7 -2
  168. package/src/gateways/ws/background-agent-protocol.js +337 -0
  169. package/src/gateways/ws/message-dispatcher.js +10 -0
  170. package/src/gateways/ws/remote-session-protocol.js +102 -1
  171. package/src/gateways/ws/ws-server.js +61 -0
  172. package/src/harness/mcp-client.js +8 -0
  173. package/src/index.js +10 -0
  174. package/src/lib/auto-mode-config.js +396 -0
  175. package/src/lib/background-agent-supervisor.js +202 -17
  176. package/src/lib/background-session-transport.js +315 -0
  177. package/src/lib/permission-denial-store.js +159 -0
  178. package/src/lib/remote-approval-bridge.js +360 -0
  179. package/src/lib/remote-control.js +270 -0
  180. package/src/lib/repl-denials.js +26 -0
  181. package/src/lib/shell-approval.js +4 -0
  182. package/src/lib/ws-rpc-client.js +150 -0
  183. package/src/repl/agent-repl.js +208 -16
  184. package/src/repl/permission-tier.js +31 -0
  185. package/src/runtime/agent-core.js +57 -0
  186. package/src/runtime/headless-runner.js +126 -8
  187. package/src/runtime/headless-stream.js +87 -1
  188. package/src/runtime/mcp-config.js +11 -0
  189. package/src/runtime/mcp-tool-search.js +0 -0
  190. package/src/runtime/policies/agent-policy.js +17 -0
  191. package/src/workers/background-agent-worker.js +218 -37
  192. package/src/assets/web-panel/assets/AppLayout-B6Q6olcr.js +0 -9
  193. package/src/assets/web-panel/assets/ChatBubbleRenderer-mDQl_PXO.js +0 -1
  194. package/src/assets/web-panel/assets/Skills-DL39dY0t.js +0 -1
  195. package/src/assets/web-panel/assets/devWarning-K_woVElh.js +0 -1
  196. package/src/assets/web-panel/assets/index-3ILvaBpo.js +0 -1
  197. package/src/assets/web-panel/assets/index-JpB-FoZq.js +0 -1
  198. package/src/assets/web-panel/assets/useFlexGapSupport-B1lWlqXN.js +0 -1
@@ -0,0 +1,499 @@
1
+ import { existsSync, readFileSync } from "node:fs";
2
+ import chalk from "chalk";
3
+ import { logger } from "../lib/logger.js";
4
+
5
+ function formatAge(ms) {
6
+ const n = Math.max(0, Math.round(Number(ms || 0) / 1000));
7
+ if (n < 60) return `${n}s`;
8
+ const m = Math.floor(n / 60);
9
+ const s = n % 60;
10
+ if (m < 60) return `${m}m${s ? ` ${s}s` : ""}`;
11
+ const h = Math.floor(m / 60);
12
+ const rest = m % 60;
13
+ return `${h}h${rest ? ` ${rest}m` : ""}`;
14
+ }
15
+
16
+ export function formatBackgroundAgentLine(session, now = Date.now()) {
17
+ const elapsed = session?.endedAt
18
+ ? session.endedAt - session.startedAt
19
+ : now - session.startedAt;
20
+ const title = session?.title ? ` ${session.title}` : "";
21
+ return `${session.id} ${String(session.status || "?").padEnd(9)} ${formatAge(elapsed).padStart(6)} ${session.cwd || ""}${title}`;
22
+ }
23
+
24
+ function printBackgroundAgents(sessions, { now = Date.now() } = {}) {
25
+ if (!sessions.length) {
26
+ logger.log(chalk.gray("No background agents."));
27
+ return;
28
+ }
29
+ for (const session of sessions) {
30
+ logger.log(formatBackgroundAgentLine(session, now));
31
+ if (session.pid || session.sessionId) {
32
+ logger.log(
33
+ chalk.gray(
34
+ ` pid ${session.pid || "?"}${session.sessionId ? ` session ${session.sessionId}` : ""}`,
35
+ ),
36
+ );
37
+ }
38
+ }
39
+ }
40
+
41
+ function formatTimestamp(value) {
42
+ const n = Number(value);
43
+ return Number.isFinite(n) && n > 0 ? new Date(n).toISOString() : "-";
44
+ }
45
+
46
+ export function formatBackgroundAgentDetails(
47
+ session,
48
+ logText = "",
49
+ options = {},
50
+ ) {
51
+ const now = typeof options.now === "number" ? options.now : Date.now();
52
+ const elapsed = session?.endedAt
53
+ ? session.endedAt - session.startedAt
54
+ : now - session.startedAt;
55
+ const lines = [
56
+ `Background agent ${session.id}`,
57
+ ` status: ${session.status || "?"}`,
58
+ ` title: ${session.title || "-"}`,
59
+ ` cwd: ${session.cwd || "-"}`,
60
+ ` pid: ${session.pid || "-"}`,
61
+ ` workerPid: ${session.workerPid || "-"}`,
62
+ ` agentPid: ${session.agentPid || "-"}`,
63
+ ` session: ${session.sessionId || "-"}`,
64
+ ` elapsed: ${formatAge(elapsed)}`,
65
+ ` startedAt: ${formatTimestamp(session.startedAt)}`,
66
+ ` endedAt: ${formatTimestamp(session.endedAt)}`,
67
+ ` log: ${session.logFile || "-"}`,
68
+ ];
69
+ if (session.phase) lines.push(` phase: ${session.phase}`);
70
+ if (Number.isFinite(Number(session.turnCount))) {
71
+ lines.push(` turns: ${session.turnCount}`);
72
+ }
73
+ if (session.transport?.pipe) {
74
+ lines.push(` transport: interactive attach available`);
75
+ }
76
+ if (session.lostReason) lines.push(` lostReason: ${session.lostReason}`);
77
+ if (Number.isFinite(Number(session.exitCode))) {
78
+ lines.push(` exitCode: ${session.exitCode}`);
79
+ }
80
+ lines.push("", "Recent output:");
81
+ lines.push(logText ? logText.trimEnd() : " (no log output)");
82
+ lines.push("", "Commands:");
83
+ lines.push(` cc attach ${session.id}`);
84
+ lines.push(` cc logs ${session.id} -n 100`);
85
+ if (session.status === "running") {
86
+ lines.push(` cc daemon stop ${session.id}`);
87
+ }
88
+ return lines.join("\n");
89
+ }
90
+
91
+ function parseLines(value, fallback) {
92
+ const n = Number(value);
93
+ return Number.isFinite(n) && n > 0 ? Math.floor(n) : fallback;
94
+ }
95
+
96
+ async function loadSupervisor() {
97
+ return import("../lib/background-agent-supervisor.js");
98
+ }
99
+
100
+ function readLogFromOffset(file, offset) {
101
+ if (!existsSync(file)) return { text: "", offset };
102
+ const text = readFileSync(file, "utf8");
103
+ if (offset > text.length) offset = 0;
104
+ return { text: text.slice(offset), offset: text.length };
105
+ }
106
+
107
+ async function followBackgroundAgent(id, options = {}) {
108
+ const {
109
+ readBackgroundAgentState,
110
+ effectiveBackgroundAgentState,
111
+ readBackgroundAgentLog,
112
+ logPath,
113
+ } = await loadSupervisor();
114
+ const lines = parseLines(options.lines, 100);
115
+ let state = effectiveBackgroundAgentState(readBackgroundAgentState(id));
116
+ if (!state) throw new Error(`Background agent not found: ${id}`);
117
+ const logFile = logPath(id);
118
+ const initial = readBackgroundAgentLog(id, { lines });
119
+ if (initial)
120
+ process.stdout.write(initial.endsWith("\n") ? initial : `${initial}\n`);
121
+ let offset = existsSync(logFile) ? readFileSync(logFile, "utf8").length : 0;
122
+
123
+ if (options.follow === false || state.status !== "running") {
124
+ return state;
125
+ }
126
+
127
+ logger.log(chalk.gray(`Attached to ${id}. Streaming logs; Ctrl-C detaches.`));
128
+ while (true) {
129
+ await new Promise((resolve) => setTimeout(resolve, 1000));
130
+ const next = readLogFromOffset(logFile, offset);
131
+ offset = next.offset;
132
+ if (next.text) process.stdout.write(next.text);
133
+ state = effectiveBackgroundAgentState(readBackgroundAgentState(id));
134
+ if (!state || state.status !== "running") {
135
+ if (state) {
136
+ logger.log(chalk.gray(`\n${id} is ${state.status}.`));
137
+ }
138
+ return state;
139
+ }
140
+ }
141
+ }
142
+
143
+ /**
144
+ * Interactive attach over the session transport: stream the log AND accept
145
+ * typed follow-up prompts (plus /stop · /status · /detach). Returns false
146
+ * when the transport is unavailable so the caller can fall back to the
147
+ * log-only follow.
148
+ */
149
+ async function interactiveAttach(id, state, options = {}) {
150
+ const { connectBackgroundSession } =
151
+ await import("../lib/background-session-transport.js");
152
+ const { readBackgroundAgentLog, logPath } = await loadSupervisor();
153
+
154
+ let closed = false;
155
+ let conn;
156
+ try {
157
+ conn = await connectBackgroundSession({
158
+ pipePath: state.transport.pipe,
159
+ token: state.transport.token,
160
+ onEvent: (message) => {
161
+ switch (message.type) {
162
+ case "turn-started":
163
+ logger.log(
164
+ chalk.gray(
165
+ `— turn ${message.turn} started${message.prompt ? `: ${message.prompt}` : ""}`,
166
+ ),
167
+ );
168
+ return;
169
+ case "turn-ended":
170
+ logger.log(
171
+ chalk.gray(
172
+ `— turn ${message.turn} ended (exit ${message.exitCode})`,
173
+ ),
174
+ );
175
+ return;
176
+ case "idle":
177
+ logger.log(
178
+ chalk.gray(
179
+ "— session idle; type a follow-up prompt, or Ctrl-C to detach (ends the session)",
180
+ ),
181
+ );
182
+ return;
183
+ case "accepted":
184
+ logger.log(chalk.gray(`— prompt queued (#${message.queued})`));
185
+ return;
186
+ case "status":
187
+ logger.log(
188
+ chalk.gray(
189
+ `— ${message.id} phase=${message.phase} turn=${message.turn}`,
190
+ ),
191
+ );
192
+ return;
193
+ case "stopping":
194
+ logger.log(chalk.gray("— stopping current turn"));
195
+ return;
196
+ case "closing":
197
+ return; // close handler prints the final state
198
+ case "error":
199
+ logger.log(chalk.yellow(`— ${message.message}`));
200
+ return;
201
+ default:
202
+ return;
203
+ }
204
+ },
205
+ onClose: () => {
206
+ closed = true;
207
+ },
208
+ });
209
+ } catch {
210
+ return false; // transport gone (worker finalizing/finalized) — fall back
211
+ }
212
+
213
+ const lines = parseLines(options.lines, 100);
214
+ const initial = readBackgroundAgentLog(id, { lines });
215
+ if (initial)
216
+ process.stdout.write(initial.endsWith("\n") ? initial : `${initial}\n`);
217
+ const logFile = logPath(id);
218
+ let offset = existsSync(logFile) ? readFileSync(logFile, "utf8").length : 0;
219
+ logger.log(
220
+ chalk.gray(
221
+ `Attached to ${id} (interactive). Type a prompt to continue the session; /stop · /status · Ctrl-C detaches.`,
222
+ ),
223
+ );
224
+
225
+ const readline = await import("node:readline");
226
+ const rl = readline.createInterface({
227
+ input: process.stdin,
228
+ output: process.stdout,
229
+ });
230
+ rl.on("line", (line) => {
231
+ const text = line.trim();
232
+ if (!text) return;
233
+ if (text === "/detach" || text === "/exit" || text === "/quit") {
234
+ rl.close();
235
+ return;
236
+ }
237
+ if (text === "/stop") {
238
+ conn.send({ type: "stop" });
239
+ return;
240
+ }
241
+ if (text === "/status") {
242
+ conn.send({ type: "status" });
243
+ return;
244
+ }
245
+ conn.send({ type: "prompt", text });
246
+ });
247
+ const detach = () => rl.close();
248
+ rl.on("SIGINT", detach);
249
+
250
+ await new Promise((resolve) => {
251
+ rl.on("close", resolve);
252
+ const poll = setInterval(() => {
253
+ const next = readLogFromOffset(logFile, offset);
254
+ offset = next.offset;
255
+ if (next.text) process.stdout.write(next.text);
256
+ if (closed) {
257
+ clearInterval(poll);
258
+ rl.close();
259
+ }
260
+ }, 500);
261
+ poll.unref?.();
262
+ rl.on("close", () => clearInterval(poll));
263
+ });
264
+
265
+ // Drain any output written between the last poll and detach.
266
+ const rest = readLogFromOffset(logFile, offset);
267
+ if (rest.text) process.stdout.write(rest.text);
268
+ try {
269
+ conn.close();
270
+ } catch {
271
+ /* already closed by the worker */
272
+ }
273
+ const { readBackgroundAgentState, effectiveBackgroundAgentState } =
274
+ await loadSupervisor();
275
+ const finalState = effectiveBackgroundAgentState(
276
+ readBackgroundAgentState(id),
277
+ );
278
+ if (finalState) logger.log(chalk.gray(`\n${id} is ${finalState.status}.`));
279
+ return true;
280
+ }
281
+
282
+ export function registerBackgroundSessionCommands(program) {
283
+ program
284
+ .command("logs <id>")
285
+ .description("Print recent output from a background agent")
286
+ .option("-n, --lines <n>", "Number of lines", "100")
287
+ .action(async (id, options) => {
288
+ try {
289
+ const { readBackgroundAgentState, readBackgroundAgentLog } =
290
+ await loadSupervisor();
291
+ if (!readBackgroundAgentState(id)) {
292
+ throw new Error(`Background agent not found: ${id}`);
293
+ }
294
+ process.stdout.write(
295
+ readBackgroundAgentLog(id, { lines: parseLines(options.lines, 100) }),
296
+ );
297
+ } catch (error) {
298
+ logger.error(chalk.red(error.message));
299
+ process.exitCode = 1;
300
+ }
301
+ });
302
+
303
+ program
304
+ .command("attach <id>")
305
+ .description(
306
+ "Attach to a background agent — interactive when its session transport is available, log stream otherwise",
307
+ )
308
+ .option("-n, --lines <n>", "Initial lines to print", "100")
309
+ .option("--no-follow", "Print the initial log tail and exit")
310
+ .option("--no-input", "Log stream only; never send prompts")
311
+ .action(async (id, options) => {
312
+ try {
313
+ const { readBackgroundAgentState, effectiveBackgroundAgentState } =
314
+ await loadSupervisor();
315
+ const state = effectiveBackgroundAgentState(
316
+ readBackgroundAgentState(id),
317
+ );
318
+ if (!state) throw new Error(`Background agent not found: ${id}`);
319
+ const wantsInteractive =
320
+ options.input !== false &&
321
+ options.follow !== false &&
322
+ state.status === "running" &&
323
+ state.transport?.pipe &&
324
+ state.transport?.token &&
325
+ process.stdin.isTTY;
326
+ if (wantsInteractive) {
327
+ const attached = await interactiveAttach(id, state, options);
328
+ if (attached) return;
329
+ // transport refused (worker mid-finalize) — fall back to logs
330
+ }
331
+ await followBackgroundAgent(id, options);
332
+ } catch (error) {
333
+ logger.error(chalk.red(error.message));
334
+ process.exitCode = 1;
335
+ }
336
+ });
337
+
338
+ const daemon = program
339
+ .command("daemon")
340
+ .description("Inspect and stop background agent supervisor sessions");
341
+
342
+ daemon
343
+ .command("status")
344
+ .description("Show background agent supervisor status")
345
+ .option("--all", "Include completed, failed, stopped and lost sessions")
346
+ .option("--json", "Output as JSON")
347
+ .action(async (options) => {
348
+ try {
349
+ const { backgroundAgentsDir, listBackgroundAgents } =
350
+ await loadSupervisor();
351
+ const sessions = listBackgroundAgents({ all: options.all === true });
352
+ const running = sessions.filter((s) => s.status === "running").length;
353
+ if (options.json) {
354
+ console.log(
355
+ JSON.stringify(
356
+ {
357
+ supervisor: "background-agents",
358
+ dir: backgroundAgentsDir(),
359
+ running,
360
+ total: sessions.length,
361
+ sessions,
362
+ },
363
+ null,
364
+ 2,
365
+ ),
366
+ );
367
+ return;
368
+ }
369
+ logger.log(
370
+ `${chalk.bold("Background agent supervisor")} ${running} running ${sessions.length} shown`,
371
+ );
372
+ logger.log(chalk.gray(` dir ${backgroundAgentsDir()}`));
373
+ printBackgroundAgents(sessions);
374
+ } catch (error) {
375
+ logger.error(chalk.red(error.message));
376
+ process.exitCode = 1;
377
+ }
378
+ });
379
+
380
+ daemon
381
+ .command("view <id>")
382
+ .description("Show a detailed background agent view")
383
+ .option("-n, --lines <n>", "Recent log lines to include", "40")
384
+ .option("--json", "Output as JSON")
385
+ .action(async (id, options) => {
386
+ try {
387
+ const {
388
+ readBackgroundAgentState,
389
+ effectiveBackgroundAgentState,
390
+ readBackgroundAgentLog,
391
+ } = await loadSupervisor();
392
+ const session = effectiveBackgroundAgentState(
393
+ readBackgroundAgentState(id),
394
+ );
395
+ if (!session) throw new Error(`Background agent not found: ${id}`);
396
+ const lines = parseLines(options.lines, 40);
397
+ const log = readBackgroundAgentLog(id, { lines });
398
+ if (options.json) {
399
+ console.log(JSON.stringify({ session, log }, null, 2));
400
+ return;
401
+ }
402
+ logger.log(formatBackgroundAgentDetails(session, log));
403
+ } catch (error) {
404
+ logger.error(chalk.red(error.message));
405
+ process.exitCode = 1;
406
+ }
407
+ });
408
+
409
+ daemon
410
+ .command("rename <id> <title...>")
411
+ .description("Rename a background agent session")
412
+ .option("--json", "Output as JSON")
413
+ .action(async (id, title, options) => {
414
+ try {
415
+ const { renameBackgroundAgent } = await loadSupervisor();
416
+ const state = renameBackgroundAgent(
417
+ id,
418
+ Array.isArray(title) ? title.join(" ") : title,
419
+ );
420
+ if (options.json) {
421
+ console.log(JSON.stringify(state, null, 2));
422
+ return;
423
+ }
424
+ logger.log(chalk.green(`Renamed background agent ${state.id}`));
425
+ logger.log(chalk.gray(` ${state.title}`));
426
+ } catch (error) {
427
+ logger.error(chalk.red(error.message));
428
+ process.exitCode = 1;
429
+ }
430
+ });
431
+
432
+ daemon
433
+ .command("resume <id> <prompt...>")
434
+ .description(
435
+ "Continue a finished/crashed background session as a new background run on the same conversation",
436
+ )
437
+ .option("--json", "Output as JSON")
438
+ .action(async (id, prompt, options) => {
439
+ try {
440
+ const { resumeBackgroundAgent } = await loadSupervisor();
441
+ const state = resumeBackgroundAgent(
442
+ id,
443
+ Array.isArray(prompt) ? prompt.join(" ") : prompt,
444
+ );
445
+ if (options.json) {
446
+ console.log(JSON.stringify(state, null, 2));
447
+ return;
448
+ }
449
+ logger.log(
450
+ chalk.green(`Resumed session as background agent ${state.id}`),
451
+ );
452
+ logger.log(chalk.gray(` session ${state.sessionId}`));
453
+ logger.log(chalk.gray(` attach: cc attach ${state.id}`));
454
+ } catch (error) {
455
+ logger.error(chalk.red(error.message));
456
+ process.exitCode = 1;
457
+ }
458
+ });
459
+
460
+ daemon
461
+ .command("stop [id]")
462
+ .description("Stop one background agent, or all running agents with --any")
463
+ .option("--any", "Stop all running background agents")
464
+ .option("--json", "Output as JSON")
465
+ .action(async (id, options) => {
466
+ try {
467
+ const { listBackgroundAgents, stopBackgroundAgent } =
468
+ await loadSupervisor();
469
+ if (!id && options.any !== true) {
470
+ throw new Error("daemon stop requires <id> or --any");
471
+ }
472
+ const targets = options.any
473
+ ? listBackgroundAgents().map((s) => s.id)
474
+ : [id];
475
+ const results = [];
476
+ for (const target of targets) {
477
+ results.push(stopBackgroundAgent(target));
478
+ }
479
+ if (options.json) {
480
+ console.log(JSON.stringify(results, null, 2));
481
+ return;
482
+ }
483
+ if (results.length === 0) {
484
+ logger.log(chalk.gray("No running background agents."));
485
+ return;
486
+ }
487
+ for (const result of results) {
488
+ if (result.stopped) {
489
+ logger.log(chalk.green(`Stopped background agent ${result.id}`));
490
+ } else {
491
+ logger.log(chalk.gray(`${result.id} is already ${result.status}`));
492
+ }
493
+ }
494
+ } catch (error) {
495
+ logger.error(chalk.red(error.message));
496
+ process.exitCode = 1;
497
+ }
498
+ });
499
+ }
@@ -103,7 +103,10 @@ export function registerCommandCommand(program) {
103
103
  .option("--no-bang", "Do not execute !`cmd` bang segments")
104
104
  .option("--output-format <fmt>", "text | json | stream-json", "text")
105
105
  .option("--model <model>", "Override the model")
106
- .option("--permission-mode <mode>", "ApprovalGate tier (see cc agent)")
106
+ .option(
107
+ "--permission-mode <mode>",
108
+ "manual | auto | dontAsk | default | plan | acceptEdits | bypassPermissions",
109
+ )
107
110
  .action(async (name, args, options) => {
108
111
  try {
109
112
  const { getCommand, expandCommand } =
@@ -136,6 +136,55 @@ export function registerPermissionsCommand(program) {
136
136
  }
137
137
  });
138
138
 
139
+ // ── recent ────────────────────────────────────────────────────────────
140
+ cmd
141
+ .command("recent")
142
+ .alias("denials")
143
+ .description("Show recent policy denials recorded by agent runs")
144
+ .option("-n, --limit <n>", "Number of denials to show", "20")
145
+ .option("--json", "Output as JSON")
146
+ .option("--clear", "Clear the recent denial log")
147
+ .action(async (options) => {
148
+ try {
149
+ const {
150
+ clearRecentDenials,
151
+ formatRecentDenials,
152
+ readRecentDenials,
153
+ recentDenialsPath,
154
+ } = await import("../lib/permission-denial-store.js");
155
+ if (options.clear) {
156
+ const result = clearRecentDenials();
157
+ if (options.json) {
158
+ console.log(JSON.stringify(result, null, 2));
159
+ } else {
160
+ logger.log(chalk.green(`Cleared ${result.file}`));
161
+ }
162
+ return;
163
+ }
164
+ const limit = Math.max(1, Number(options.limit) || 20);
165
+ const denials = readRecentDenials({ limit });
166
+ if (options.json) {
167
+ console.log(
168
+ JSON.stringify(
169
+ {
170
+ file: recentDenialsPath(),
171
+ count: denials.length,
172
+ denials,
173
+ },
174
+ null,
175
+ 2,
176
+ ),
177
+ );
178
+ return;
179
+ }
180
+ logger.log(formatRecentDenials(denials));
181
+ logger.log(chalk.gray(` source: ${recentDenialsPath()}`));
182
+ } catch (err) {
183
+ logger.error(chalk.red(`permissions recent failed: ${err.message}`));
184
+ process.exitCode = 1;
185
+ }
186
+ });
187
+
139
188
  // ── test (dry-run) ─────────────────────────────────────────────────────
140
189
  cmd
141
190
  .command("test <tool> [args...]")