chainlesschain 0.162.156 → 0.162.158

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 (195) hide show
  1. package/README.md +1 -1
  2. package/package.json +2 -2
  3. package/src/assets/web-panel/.build-hash +1 -1
  4. package/src/assets/web-panel/assets/{AIOps-DjBXlypQ.js → AIOps-BwpTJTN_.js} +1 -1
  5. package/src/assets/web-panel/assets/{ActionButton-Cv9er9bp.js → ActionButton-BtQOoo3l.js} +1 -1
  6. package/src/assets/web-panel/assets/{Analytics-CLKDjSNs.js → Analytics-Duf_rXZa.js} +3 -3
  7. package/src/assets/web-panel/assets/AppLayout-XQK3wjvB.js +9 -0
  8. package/src/assets/web-panel/assets/Artifacts-CTVt96zW.css +1 -0
  9. package/src/assets/web-panel/assets/Artifacts-DQvJoQ6E.js +1 -0
  10. package/src/assets/web-panel/assets/{Audit-CXafD178.js → Audit-DNjOGQLh.js} +1 -1
  11. package/src/assets/web-panel/assets/{BackgroundAgents-CP5c-GSd.js → BackgroundAgents-FH9RAfB5.js} +1 -1
  12. package/src/assets/web-panel/assets/{Backup-BbYLQfyt.js → Backup-ZIbrR4-U.js} +1 -1
  13. package/src/assets/web-panel/assets/{BaseInput-Bqe0MwZL.js → BaseInput-DSt3NaND.js} +1 -1
  14. package/src/assets/web-panel/assets/{Chat-jNA_yUPH.js → Chat-CY-wvbjv.js} +6 -6
  15. package/src/assets/web-panel/assets/Chat-KSGPtU0L.css +1 -0
  16. package/src/assets/web-panel/assets/{ChatBubbleRenderer-BOFLF01s.js → ChatBubbleRenderer-BAV9V-t-.js} +1 -1
  17. package/src/assets/web-panel/assets/{Checkbox-Dlzz-zD1.js → Checkbox-8JVadE_B.js} +1 -1
  18. package/src/assets/web-panel/assets/{Codegen-BAsDQjGf.js → Codegen-CN83nPYI.js} +1 -1
  19. package/src/assets/web-panel/assets/{Col-Ju0HrAuS.js → Col-xPSvqdN8.js} +1 -1
  20. package/src/assets/web-panel/assets/{Community-DynJd43Z.js → Community-D78cnIjG.js} +1 -1
  21. package/src/assets/web-panel/assets/{Compact-CabbZVww.js → Compact-CSw6Csbp.js} +1 -1
  22. package/src/assets/web-panel/assets/{Compliance-DW4BmAoh.js → Compliance-E_HhjcYR.js} +1 -1
  23. package/src/assets/web-panel/assets/{Cowork-Bk1S_7X-.js → Cowork-DybeQjmm.js} +3 -3
  24. package/src/assets/web-panel/assets/{Cron-By4sh_NM.js → Cron-DS0BJeLY.js} +2 -2
  25. package/src/assets/web-panel/assets/{Crosschain-DdZdbS1C.js → Crosschain-BOwVgkbs.js} +1 -1
  26. package/src/assets/web-panel/assets/{DID-CkL-sQyS.js → DID-DRUr9YuX.js} +2 -2
  27. package/src/assets/web-panel/assets/{Dashboard-g_mOpumg.js → Dashboard-Cht-yUKW.js} +2 -2
  28. package/src/assets/web-panel/assets/{Dropdown-DtHyE7Ao.js → Dropdown-BLr_EJ9X.js} +1 -1
  29. package/src/assets/web-panel/assets/{EmailListRenderer-Kpvov5Wr.js → EmailListRenderer-Drexuh5W.js} +1 -1
  30. package/src/assets/web-panel/assets/{FamilyGuardDashboard-C4kZoePR.js → FamilyGuardDashboard-D9Q2Kq63.js} +1 -1
  31. package/src/assets/web-panel/assets/{Federation-BAwWoK2N.js → Federation-C-Z1xI-T.js} +1 -1
  32. package/src/assets/web-panel/assets/{FormItemContext-DeI0i5f8.js → FormItemContext-DLHPMkQF.js} +1 -1
  33. package/src/assets/web-panel/assets/{GenericCardRenderer-CkcKRtos.js → GenericCardRenderer-DxxEdn2r.js} +1 -1
  34. package/src/assets/web-panel/assets/{Git-kX6jjI9y.js → Git-syATcaBt.js} +2 -2
  35. package/src/assets/web-panel/assets/{Governance-BXJCBm6x.js → Governance-CYe_r8Zf.js} +1 -1
  36. package/src/assets/web-panel/assets/Inference-GWxErB7k.js +1 -0
  37. package/src/assets/web-panel/assets/{KnowledgeGraph-3Dnuf1Wh.js → KnowledgeGraph-qQvVCZoi.js} +1 -1
  38. package/src/assets/web-panel/assets/{Logs-lVAZCtUB.js → Logs-BenAU2W8.js} +2 -2
  39. package/src/assets/web-panel/assets/MarkdownRenderer-7agE3Or1.js +1 -0
  40. package/src/assets/web-panel/assets/MarkdownRenderer-DbzhFwvz.css +1 -0
  41. package/src/assets/web-panel/assets/Marketplace-D4Jn5fpD.js +1 -0
  42. package/src/assets/web-panel/assets/{McpTools-CqjzefRU.js → McpTools-DtAwoqiQ.js} +4 -4
  43. package/src/assets/web-panel/assets/{Memory-BFiPAxk6.js → Memory-BI_O50I8.js} +2 -2
  44. package/src/assets/web-panel/assets/{MobileBridge-D0O43VwF.js → MobileBridge-CQWE2lqx.js} +1 -1
  45. package/src/assets/web-panel/assets/{MobileProjects-B3now48t.js → MobileProjects-CVtTqgtg.js} +1 -1
  46. package/src/assets/web-panel/assets/{Mtc-DybhtaCS.js → Mtc-Dnr16qsn.js} +2 -2
  47. package/src/assets/web-panel/assets/{MtcAudit-D7MmY4F0.js → MtcAudit-CQ5Xe4-a.js} +5 -5
  48. package/src/assets/web-panel/assets/{Multisig-wZvGJnB8.js → Multisig-Bjr8O-wy.js} +3 -3
  49. package/src/assets/web-panel/assets/{NLProgramming-BNPpgfvn.js → NLProgramming-CEe-KirU.js} +1 -1
  50. package/src/assets/web-panel/assets/{Notes-B6FS3KBj.js → Notes-spKJIKF9.js} +4 -4
  51. package/src/assets/web-panel/assets/{NotificationSettings-CK0yla9U.js → NotificationSettings-DRp83LFG.js} +1 -1
  52. package/src/assets/web-panel/assets/OrderTableRenderer-h1A0XB2a.js +1 -0
  53. package/src/assets/web-panel/assets/{Organization-geqgwN1T.js → Organization-BlgB0IYg.js} +4 -4
  54. package/src/assets/web-panel/assets/{Overflow-BWJYZFbC.js → Overflow-a_35HqZI.js} +1 -1
  55. package/src/assets/web-panel/assets/{P2P-DnU1505F.js → P2P-CSNHCNPC.js} +2 -2
  56. package/src/assets/web-panel/assets/{PdhVaultBrowser-BGY74KnC.js → PdhVaultBrowser-C8M1JH-1.js} +5 -5
  57. package/src/assets/web-panel/assets/{Permissions-CgArrWtQ.js → Permissions-DmjJi9Ms.js} +4 -4
  58. package/src/assets/web-panel/assets/{PersonalDataHub-C2vYE61a.js → PersonalDataHub-DfuwRyf4.js} +2 -2
  59. package/src/assets/web-panel/assets/{Pipeline-CBYLI4yW.js → Pipeline-B2-S4ekE.js} +1 -1
  60. package/src/assets/web-panel/assets/{Privacy-B-PdW9aN.js → Privacy-B1dOiBLC.js} +1 -1
  61. package/src/assets/web-panel/assets/{ProjectInit-CuWQegbV.js → ProjectInit-_pNmc-DX.js} +2 -2
  62. package/src/assets/web-panel/assets/{ProjectSettings-4d32m_cS.js → ProjectSettings-DmwG2Ii1.js} +2 -2
  63. package/src/assets/web-panel/assets/{Projects-VLA7ttwd.js → Projects-COATmHxF.js} +1 -1
  64. package/src/assets/web-panel/assets/{Providers-B_Izb_oa.js → Providers-C6HSjg1d.js} +1 -1
  65. package/src/assets/web-panel/assets/{QrScannerModal-C6B_ndXV.js → QrScannerModal-CrDmS7l1.js} +1 -1
  66. package/src/assets/web-panel/assets/{QuickAsk-C6tavL-y.js → QuickAsk-CfKvWt-x.js} +1 -1
  67. package/src/assets/web-panel/assets/{Recommend-CF-LaZ6x.js → Recommend-DMa6Kuzv.js} +1 -1
  68. package/src/assets/web-panel/assets/RemoteSession-DrktUo5x.js +4 -0
  69. package/src/assets/web-panel/assets/Reputation-BTPQ1vjL.js +1 -0
  70. package/src/assets/web-panel/assets/{Row-DO0fd48A.js → Row-B6Q7QkMG.js} +1 -1
  71. package/src/assets/web-panel/assets/{RssFeed-BV5LDzFR.js → RssFeed-B1IJhAmX.js} +3 -3
  72. package/src/assets/web-panel/assets/Search-xbea1kGD.js +1 -0
  73. package/src/assets/web-panel/assets/{Security-DwSrWRCw.js → Security-7rs0cO3F.js} +4 -4
  74. package/src/assets/web-panel/assets/{Services-BEf1QyM4.js → Services-0fjixK0S.js} +2 -2
  75. package/src/assets/web-panel/assets/{Skeleton-LZXTpovy.js → Skeleton-B_rVq9Cu.js} +1 -1
  76. package/src/assets/web-panel/assets/{Skills-SKgc0Zug.js → Skills-C5uM90b9.js} +1 -1
  77. package/src/assets/web-panel/assets/{Sla-C_QVOtkN.js → Sla-U4pYCIZH.js} +1 -1
  78. package/src/assets/web-panel/assets/{SpeechSettings-v5TW2V-X.js → SpeechSettings-Cdz4yTWx.js} +1 -1
  79. package/src/assets/web-panel/assets/{SyncSettings-CQPjHdqU.js → SyncSettings-Me3aetHv.js} +2 -2
  80. package/src/assets/web-panel/assets/{Tasks-DCd-uYcI.js → Tasks-Cf6aNkL_.js} +1 -1
  81. package/src/assets/web-panel/assets/{Templates-62VDIjtj.js → Templates-CYatk4tU.js} +1 -1
  82. package/src/assets/web-panel/assets/{Tenant-BnFQ0pNy.js → Tenant-DIMKLw7o.js} +1 -1
  83. package/src/assets/web-panel/assets/{Terminal-D3v0MGSj.js → Terminal-C_cp-uSJ.js} +2 -2
  84. package/src/assets/web-panel/assets/{TimelineRenderer-BI1Ka-cu.js → TimelineRenderer-cUPVNADp.js} +1 -1
  85. package/src/assets/web-panel/assets/{Tokens-BwX1jXYP.js → Tokens-CiaEIXVf.js} +1 -1
  86. package/src/assets/web-panel/assets/{Trigger-CSA8TqNx.js → Trigger-DB1WU5lQ.js} +1 -1
  87. package/src/assets/web-panel/assets/{Trust-Bn3IjCx7.js → Trust-Cfht9Ijy.js} +1 -1
  88. package/src/assets/web-panel/assets/{UkeySign-pUbOAqNi.js → UkeySign-K3P4Vt9S.js} +1 -1
  89. package/src/assets/web-panel/assets/{VideoEditing-Db2RercF.js → VideoEditing-DWjhjXRh.js} +1 -1
  90. package/src/assets/web-panel/assets/{Wallet-CpR1BVAl.js → Wallet-D9-Q1Kan.js} +4 -4
  91. package/src/assets/web-panel/assets/{WebAuthn-Yxtkn7Zp.js → WebAuthn-BDszy-oV.js} +5 -5
  92. package/src/assets/web-panel/assets/{WorkflowEditor-CYLe9a2E.js → WorkflowEditor-DIhij4rA.js} +1 -1
  93. package/src/assets/web-panel/assets/{chat-gsOGdak0.js → chat-D7VCiKbh.js} +1 -1
  94. package/src/assets/web-panel/assets/{colors-D9fN299a.js → colors-BvOd9pVn.js} +1 -1
  95. package/src/assets/web-panel/assets/{compact-item-vkd7s-ay.js → compact-item-DoqXBiAO.js} +1 -1
  96. package/src/assets/web-panel/assets/{createContext-C7YkbMyy.js → createContext-CrKRzUtp.js} +1 -1
  97. package/src/assets/web-panel/assets/devWarning-nDB--2xr.js +1 -0
  98. package/src/assets/web-panel/assets/{hasIn-ig7reRSU.js → hasIn-BWAev4WC.js} +1 -1
  99. package/src/assets/web-panel/assets/{icons-DEmcnxBf.js → icons-DG5UD-6K.js} +4 -4
  100. package/src/assets/web-panel/assets/{index-W52sHRdr.js → index-1poMpMU0.js} +1 -1
  101. package/src/assets/web-panel/assets/{index-DZAxCnjS.js → index-8g5TJCZ3.js} +1 -1
  102. package/src/assets/web-panel/assets/{index-9TxdMYju.js → index-B0vGEbJT.js} +1 -1
  103. package/src/assets/web-panel/assets/index-B1_pRTtB.js +1 -0
  104. package/src/assets/web-panel/assets/{index-DhdXbsXX.js → index-B30NvR21.js} +1 -1
  105. package/src/assets/web-panel/assets/{index-B7QRpKif.js → index-BCyP071m.js} +1 -1
  106. package/src/assets/web-panel/assets/{index-Bt-9ZhIu.js → index-BHOcVYrx.js} +1 -1
  107. package/src/assets/web-panel/assets/{index-BYe9HRSt.js → index-BN9m4Ux2.js} +1 -1
  108. package/src/assets/web-panel/assets/{index-BkP17Z95.js → index-BWWsaimP.js} +1 -1
  109. package/src/assets/web-panel/assets/{index-CDWTycrg.js → index-BXyl8O23.js} +1 -1
  110. package/src/assets/web-panel/assets/{index-CspGymmg.js → index-BZGk8AbZ.js} +1 -1
  111. package/src/assets/web-panel/assets/{index-DFfxF7Vt.js → index-Bf9YlbDn.js} +1 -1
  112. package/src/assets/web-panel/assets/{index-DQ8U7kzC.js → index-Bj7EgmOp.js} +1 -1
  113. package/src/assets/web-panel/assets/{index-D2yhtk6a.js → index-BkgnGX7v.js} +1 -1
  114. package/src/assets/web-panel/assets/{index-FzGr6CVk.js → index-BoYrS_L2.js} +1 -1
  115. package/src/assets/web-panel/assets/{index-BMw48d19.js → index-Bq_ctRpz.js} +1 -1
  116. package/src/assets/web-panel/assets/{index-CAkz1f1r.js → index-C5kRmj3P.js} +1 -1
  117. package/src/assets/web-panel/assets/{index-SDrcQXO1.js → index-CDGLdn12.js} +1 -1
  118. package/src/assets/web-panel/assets/{index-gj8kxcAi.js → index-CEPHMSih.js} +1 -1
  119. package/src/assets/web-panel/assets/{index-PkH39qsp.js → index-CEXfKSiI.js} +1 -1
  120. package/src/assets/web-panel/assets/{index-BHHN_KTS.js → index-CSb-tnIo.js} +1 -1
  121. package/src/assets/web-panel/assets/{index-gpurplyq.js → index-CmJ6xKBO.js} +1 -1
  122. package/src/assets/web-panel/assets/{index-BfpVcXKa.js → index-CoD31Ndm.js} +1 -1
  123. package/src/assets/web-panel/assets/{index-CGzGe5tv.js → index-Cx7M7PSF.js} +1 -1
  124. package/src/assets/web-panel/assets/{index-BhftELHI.js → index-D23L1V76.js} +3 -3
  125. package/src/assets/web-panel/assets/{index-DPguq3X7.js → index-DV-Rfmfx.js} +4 -4
  126. package/src/assets/web-panel/assets/{index-BpGlLbRv.js → index-DlGu7As6.js} +1 -1
  127. package/src/assets/web-panel/assets/{index-C8HZMygt.js → index-DvDOpsJB.js} +1 -1
  128. package/src/assets/web-panel/assets/{index-DuwbKHoS.js → index-HwHdTusY.js} +1 -1
  129. package/src/assets/web-panel/assets/{index-C7AY39sg.js → index-ISvPZ05r.js} +1 -1
  130. package/src/assets/web-panel/assets/{index-nX8YE6cj.js → index-JzUmJMvn.js} +1 -1
  131. package/src/assets/web-panel/assets/{index-DvCJXmrx.js → index-KR1-Em9C.js} +1 -1
  132. package/src/assets/web-panel/assets/{index-Co2NYyew.js → index-PDxGjKRu.js} +1 -1
  133. package/src/assets/web-panel/assets/index-TMM9FuW-.js +1 -0
  134. package/src/assets/web-panel/assets/{index-D6s_kbjt.js → index-YGDy7nNq.js} +1 -1
  135. package/src/assets/web-panel/assets/{index-Dy_TKZ9h.js → index-gmxqu5Kk.js} +1 -1
  136. package/src/assets/web-panel/assets/{index-DFM1oNe1.js → index-oY45XrkG.js} +1 -1
  137. package/src/assets/web-panel/assets/{index-bVbgYBhv.js → index-ww1QtLnX.js} +1 -1
  138. package/src/assets/web-panel/assets/{index-BvDEukXe.js → index-zH-tuuGZ.js} +1 -1
  139. package/src/assets/web-panel/assets/{initDefaultProps-BrgfVRg9.js → initDefaultProps-XW-OR3mL.js} +1 -1
  140. package/src/assets/web-panel/assets/{motion-D_3xmwac.js → motion-CeuEHh20.js} +1 -1
  141. package/src/assets/web-panel/assets/{move-DDDJdlxw.js → move-Dva79Keg.js} +1 -1
  142. package/src/assets/web-panel/assets/{mtc-parser-BK2uEWwn.js → mtc-parser-C0B6bD2j.js} +1 -1
  143. package/src/assets/web-panel/assets/{omit-BPHKbDcf.js → omit-AkZvO-a2.js} +1 -1
  144. package/src/assets/web-panel/assets/{pickAttrs-LNnTscuz.js → pickAttrs-B4r55qGc.js} +1 -1
  145. package/src/assets/web-panel/assets/{placementArrow-CvmTZll5.js → placementArrow-BLH-z7YE.js} +1 -1
  146. package/src/assets/web-panel/assets/{responsiveObserve-eBj7qi2J.js → responsiveObserve-ksEGZ2Wf.js} +1 -1
  147. package/src/assets/web-panel/assets/{slide-D68hHkrM.js → slide-Cth4OD1Z.js} +1 -1
  148. package/src/assets/web-panel/assets/{statusUtils-daTlaeo0.js → statusUtils-9S83ezxw.js} +1 -1
  149. package/src/assets/web-panel/assets/{styleChecker-DtyKMtHB.js → styleChecker-7yQmQGZ4.js} +1 -1
  150. package/src/assets/web-panel/assets/{useFlexGapSupport-ekFwFs9h.js → useFlexGapSupport-DpGnMmPc.js} +1 -1
  151. package/src/assets/web-panel/assets/{useFs-jRaSEoF4.js → useFs-C4zDZIXH.js} +1 -1
  152. package/src/assets/web-panel/assets/{usePersonalDataHub-DD5nGzjA.js → usePersonalDataHub-CoLICdUZ.js} +1 -1
  153. package/src/assets/web-panel/assets/{vnode-KcgLUiom.js → vnode-CKyH-35X.js} +1 -1
  154. package/src/assets/web-panel/assets/{zoom-Cyk2PQ-s.js → zoom-DRDGit17.js} +1 -1
  155. package/src/assets/web-panel/index.html +2 -2
  156. package/src/command-manifest.json +8 -1
  157. package/src/commands/agent.js +49 -5
  158. package/src/commands/artifacts.js +161 -0
  159. package/src/commands/browse.js +204 -2
  160. package/src/data/changelog.json +13 -2
  161. package/src/gateways/ws/artifact-protocol.js +219 -0
  162. package/src/gateways/ws/message-dispatcher.js +6 -0
  163. package/src/gateways/ws/ws-server.js +32 -0
  164. package/src/index.js +6 -0
  165. package/src/lib/accessibility.js +55 -0
  166. package/src/lib/artifact-store.js +236 -0
  167. package/src/lib/chrome-connector.js +349 -0
  168. package/src/lib/hook-runner.cjs +47 -7
  169. package/src/lib/ide-context.js +84 -0
  170. package/src/lib/permission-rules.cjs +26 -3
  171. package/src/lib/plugin-runtime/scopes.js +12 -0
  172. package/src/lib/remote-control.js +35 -0
  173. package/src/lib/safe-mode.js +40 -0
  174. package/src/lib/shell-selector.cjs +203 -0
  175. package/src/lib/skill-loader.js +19 -0
  176. package/src/lib/slash-dispatch.js +34 -0
  177. package/src/lib/web-ui-server.js +120 -7
  178. package/src/repl/agent-repl.js +330 -116
  179. package/src/repl/remote-approval.js +111 -0
  180. package/src/runtime/agent-core.js +129 -8
  181. package/src/runtime/coding-agent-contract-shared.cjs +105 -0
  182. package/src/runtime/coding-agent-policy.cjs +31 -0
  183. package/src/runtime/headless-stream.js +51 -1
  184. package/src/runtime/policies/agent-policy.js +4 -0
  185. package/src/assets/web-panel/assets/AppLayout-CfyZNzmJ.js +0 -9
  186. package/src/assets/web-panel/assets/Chat-DDh58PCf.css +0 -1
  187. package/src/assets/web-panel/assets/Inference-ujD2JSKo.js +0 -1
  188. package/src/assets/web-panel/assets/Marketplace-qnzNPm-A.js +0 -1
  189. package/src/assets/web-panel/assets/OrderTableRenderer-CXfPHIpy.js +0 -1
  190. package/src/assets/web-panel/assets/RemoteSession-DBqTTQ9_.js +0 -4
  191. package/src/assets/web-panel/assets/Reputation-B01FALUi.js +0 -1
  192. package/src/assets/web-panel/assets/Search-CTti4_MP.js +0 -1
  193. package/src/assets/web-panel/assets/devWarning-DEcR2h6k.js +0 -1
  194. package/src/assets/web-panel/assets/index-BEibanjg.js +0 -1
  195. package/src/assets/web-panel/assets/index-z_25tU3p.js +0 -1
@@ -0,0 +1,111 @@
1
+ /**
2
+ * REPL-side remote-approval glue (gap-analysis 批26 — 批17/18 REPL 遗留收口).
3
+ *
4
+ * Interactive sessions race the LOCAL terminal prompt against a paired
5
+ * device's decision (headless gates on the remote decision alone):
6
+ *
7
+ * - local answer first → settle the device card too (resolveLocally
8
+ * publishes permission.resolved so the phone/web UI clears it)
9
+ * - remote answer first → cancel the local readline prompt and print
10
+ * the outcome
11
+ * - remote leg times out or the bridge closes mid-ask → NOT a decision:
12
+ * keep waiting on the terminal. A silent phone must never auto-deny a
13
+ * user sitting at the keyboard — the fail-closed timeout is headless
14
+ * semantics only.
15
+ *
16
+ * Pure over an injected bridge + local prompt handle ({promise, cancel}),
17
+ * so the race is unit-testable without a WS server or readline.
18
+ */
19
+
20
+ /**
21
+ * Normalize the shapes the REPL's two confirmers receive (ApprovalGate
22
+ * `{tool, args, riskLevel}` / settings-hook `{tool, args, rule, reason}`)
23
+ * into the bridge's `{tool, action, detail}` ask payload.
24
+ */
25
+ export function describeAskContext({
26
+ tool,
27
+ args,
28
+ rule = null,
29
+ reason = null,
30
+ riskLevel = null,
31
+ } = {}) {
32
+ const detail =
33
+ typeof args?.command === "string"
34
+ ? args.command
35
+ : typeof args?.path === "string"
36
+ ? args.path
37
+ : args
38
+ ? JSON.stringify(args).slice(0, 2000)
39
+ : null;
40
+ return {
41
+ tool: tool || null,
42
+ action: rule
43
+ ? `ask-rule:${rule}`
44
+ : reason || (riskLevel ? `${riskLevel}-risk` : null),
45
+ detail,
46
+ };
47
+ }
48
+
49
+ // Remote outcomes that are NOT a decision for an interactive session: the
50
+ // devices went silent (timeout) or the bridge was torn down mid-ask (closed).
51
+ const NON_DECISIVE_REMOTE = new Set(["timeout", "closed"]);
52
+
53
+ /**
54
+ * Race the local interactive prompt against the paired-device decision.
55
+ *
56
+ * @param {object} opts
57
+ * @param {import("../lib/remote-approval-bridge.js").RemoteApprovalBridge} opts.bridge
58
+ * @param {{tool?, action?, detail?}} opts.ask - describeAskContext() payload
59
+ * @param {{promise: Promise<boolean>, cancel?: () => void}} opts.local -
60
+ * the already-started local prompt; `cancel` closes its readline when the
61
+ * device answers first (implementations must mute their idle-timeout
62
+ * message after cancel).
63
+ * @param {(text: string) => void} [opts.writeOut]
64
+ * @returns {Promise<boolean>} approved
65
+ */
66
+ export async function raceLocalAndRemote({
67
+ bridge,
68
+ ask = {},
69
+ local,
70
+ writeOut = () => {},
71
+ }) {
72
+ let requestId = null;
73
+ const remoteLeg = bridge
74
+ .requestDecision({
75
+ ...ask,
76
+ onRequestId: (id) => {
77
+ requestId = id;
78
+ },
79
+ })
80
+ .then((decision) => ({ src: "remote", decision }));
81
+ const localLeg = local.promise.then(
82
+ (approved) => ({ src: "local", approved: approved === true }),
83
+ () => ({ src: "local", approved: false }),
84
+ );
85
+ let winner = await Promise.race([remoteLeg, localLeg]);
86
+ if (
87
+ winner.src === "remote" &&
88
+ NON_DECISIVE_REMOTE.has(winner.decision?.via)
89
+ ) {
90
+ winner = await localLeg;
91
+ }
92
+ if (winner.src === "local") {
93
+ if (requestId) {
94
+ try {
95
+ bridge.resolveLocally(requestId, winner.approved);
96
+ } catch {
97
+ // clearing the device card is best-effort
98
+ }
99
+ }
100
+ return winner.approved;
101
+ }
102
+ try {
103
+ local.cancel?.();
104
+ } catch {
105
+ // readline already closed
106
+ }
107
+ writeOut(
108
+ `\n ✓ ${winner.decision.approved ? "allowed" : "denied"} from paired device\n`,
109
+ );
110
+ return winner.decision.approved === true;
111
+ }
@@ -1041,6 +1041,7 @@ async function tryIdeDiffApprovalForEdit(
1041
1041
  hasIdeOpenDiff,
1042
1042
  requestIdeDiffApproval,
1043
1043
  formatReviewComments,
1044
+ summarizeUserAmendments,
1044
1045
  } = await import("../lib/ide-context.js");
1045
1046
  const mcpLike = {
1046
1047
  mcpClient: context.mcpClient,
@@ -1056,15 +1057,22 @@ async function tryIdeDiffApprovalForEdit(
1056
1057
  title: `cc agent: ${name} ${path.basename(proposal.filePath)}`,
1057
1058
  });
1058
1059
  if (verdict?.outcome === "accepted") {
1060
+ // When the reviewer amended the proposal in the diff before accepting,
1061
+ // hand the agent the actual -/+ delta — not just a flag — so its model
1062
+ // of the file matches what was really written (gap #4: the agent
1063
+ // perceives the user's edits).
1064
+ const amendments =
1065
+ verdict.finalText != null && verdict.finalText !== proposal.newContent
1066
+ ? summarizeUserAmendments(proposal.newContent, verdict.finalText)
1067
+ : null;
1059
1068
  return {
1060
1069
  outcome: "accepted",
1061
1070
  result: {
1062
1071
  success: true,
1063
1072
  path: proposal.filePath,
1064
1073
  appliedVia: "ide-diff",
1065
- ...(verdict.finalText != null &&
1066
- verdict.finalText !== proposal.newContent
1067
- ? { userEdited: true }
1074
+ ...(amendments
1075
+ ? { userEdited: true, userAmendments: amendments }
1068
1076
  : {}),
1069
1077
  policy: { decision: "allow", rule, via: "ide-diff" },
1070
1078
  },
@@ -2431,6 +2439,24 @@ async function executeToolInner(
2431
2439
  }
2432
2440
  }
2433
2441
 
2442
+ // P1 #8 Windows/PowerShell first-class: per-call args.shell or the
2443
+ // configured settings `shell.windowsDefault` may route this command
2444
+ // through PowerShell via explicit argv (`powershell.exe -NoProfile
2445
+ // [-ExecutionPolicy <p>] -Command <cmd>`). The unconfigured path keeps
2446
+ // the historical default shell byte-identical (useDefaultShell=true).
2447
+ const { resolveShellInvocation } =
2448
+ await import("../lib/shell-selector.cjs");
2449
+ const shellInv = resolveShellInvocation({
2450
+ command: args.command,
2451
+ requested: args.shell,
2452
+ cwd: args.cwd || cwd,
2453
+ });
2454
+ const shellMeta = shellInv.useDefaultShell
2455
+ ? shellInv.note
2456
+ ? { shell_note: shellInv.note }
2457
+ : {}
2458
+ : { shell: shellInv.kind };
2459
+
2434
2460
  // Background: spawn, register, return a task_id immediately. The agent
2435
2461
  // polls output + completion via check_shell. No timeout — that's the whole
2436
2462
  // point of backgrounding (builds, test suites, dev servers).
@@ -2463,9 +2489,9 @@ async function executeToolInner(
2463
2489
  child: null,
2464
2490
  };
2465
2491
  try {
2466
- const child = spawn(args.command, {
2492
+ const bgSpawnOpts = {
2467
2493
  cwd: task.cwd,
2468
- shell: true,
2494
+ shell: shellInv.useDefaultShell,
2469
2495
  windowsHide: true,
2470
2496
  // Same agent-identity env as the foreground path: CLAUDECODE marks
2471
2497
  // "running under the agent"; the session id correlates work to the
@@ -2484,7 +2510,12 @@ async function executeToolInner(
2484
2510
  // the whole tree (shell + its grandchild command). No-op on Windows
2485
2511
  // where the tree is killed via taskkill /T instead.
2486
2512
  detached: process.platform !== "win32",
2487
- });
2513
+ };
2514
+ // PowerShell route: explicit argv (shell:false above); default route
2515
+ // is the historical spawn(command, {shell:true}) byte-for-byte.
2516
+ const child = shellInv.useDefaultShell
2517
+ ? spawn(args.command, bgSpawnOpts)
2518
+ : spawn(shellInv.file, shellInv.argv, bgSpawnOpts);
2488
2519
  task.child = child;
2489
2520
  if (child.stdout) {
2490
2521
  child.stdout.setEncoding("utf8");
@@ -2532,6 +2563,7 @@ async function executeToolInner(
2532
2563
  task_id: id,
2533
2564
  status: task.status,
2534
2565
  command: task.command,
2566
+ ...shellMeta,
2535
2567
  hint: "Poll output and completion with the check_shell tool using this task_id. Kill long-lived servers with check_shell { task_id, kill: true } when done.",
2536
2568
  shellCommandPolicy: shellPolicy,
2537
2569
  approval: approvalOutcome,
@@ -2630,7 +2662,7 @@ async function executeToolInner(
2630
2662
  }
2631
2663
 
2632
2664
  try {
2633
- const output = execSync(args.command, {
2665
+ const fgExecOpts = {
2634
2666
  cwd: args.cwd || cwd,
2635
2667
  encoding: "utf8",
2636
2668
  timeout: _resolveShellTimeout(args.timeout),
@@ -2649,10 +2681,35 @@ async function executeToolInner(
2649
2681
  }
2650
2682
  : {}),
2651
2683
  },
2652
- });
2684
+ };
2685
+ let output;
2686
+ if (shellInv.useDefaultShell) {
2687
+ output = execSync(args.command, fgExecOpts);
2688
+ } else {
2689
+ // PowerShell route: explicit argv, no intermediate default shell.
2690
+ // Reproduce execSync's contract so the shared catch shapes errors
2691
+ // identically: throw on spawn error; throw an Error carrying
2692
+ // status/stdout/stderr on non-zero exit.
2693
+ const res = spawnSync(shellInv.file, shellInv.argv, {
2694
+ ...fgExecOpts,
2695
+ windowsHide: true,
2696
+ });
2697
+ if (res.error) throw res.error;
2698
+ if (res.status !== 0) {
2699
+ const e = new Error(
2700
+ `Command failed (${shellInv.kind}, exit ${res.status}): ${args.command}`,
2701
+ );
2702
+ e.status = res.status;
2703
+ e.stdout = res.stdout;
2704
+ e.stderr = res.stderr;
2705
+ throw e;
2706
+ }
2707
+ output = res.stdout ?? "";
2708
+ }
2653
2709
  return attachDescriptor(
2654
2710
  {
2655
2711
  stdout: output.substring(0, 30000),
2712
+ ...shellMeta,
2656
2713
  shellCommandPolicy: shellPolicy,
2657
2714
  approval: approvalOutcome,
2658
2715
  },
@@ -2672,6 +2729,7 @@ async function executeToolInner(
2672
2729
  : {}),
2673
2730
  stderr: (err.stderr || "").substring(0, 2000),
2674
2731
  exitCode: err.status,
2732
+ ...shellMeta,
2675
2733
  shellCommandPolicy: shellPolicy,
2676
2734
  approval: approvalOutcome,
2677
2735
  },
@@ -3104,6 +3162,65 @@ async function executeToolInner(
3104
3162
  }
3105
3163
  }
3106
3164
 
3165
+ case "publish_artifact": {
3166
+ // P1 #10: copy a finished deliverable into the user's artifact store.
3167
+ // Only METADATA returns to the conversation (the transcript never
3168
+ // carries the file body); `cc artifacts` lists/inspects/cleans.
3169
+ try {
3170
+ const { ArtifactStore } = await import("../lib/artifact-store.js");
3171
+ const store = new ArtifactStore();
3172
+ const entry = store.publish({
3173
+ filePath: path.resolve(cwd, String(args.path || "")),
3174
+ title: args.title,
3175
+ kind: args.kind,
3176
+ ttlDays: args.ttl_days,
3177
+ sessionId: sessionId ? String(sessionId) : null,
3178
+ });
3179
+ return attachDescriptor({
3180
+ published: entry,
3181
+ hint: "The user can browse this with `cc artifacts list` / `cc artifacts show <id>`.",
3182
+ });
3183
+ } catch (err) {
3184
+ return attachDescriptor({
3185
+ error: `publish_artifact failed: ${err.message}`,
3186
+ });
3187
+ }
3188
+ }
3189
+
3190
+ case "browser_state": {
3191
+ // P1 #8: first-class Chrome CDP observation — the agent side of the
3192
+ // "locate error → fix → verify" web loop against the user's real,
3193
+ // logged-in browser (`cc browse chrome launch`). DOM is capped harder
3194
+ // than the CLI command's 150k default so a page dump cannot flood the
3195
+ // conversation; screenshots go to a generated temp file (never an
3196
+ // agent-chosen path) to keep the tool strictly read-only.
3197
+ try {
3198
+ const { captureState } = await import("../lib/chrome-connector.js");
3199
+ const screenshotPath = args.screenshot
3200
+ ? path.join(os.tmpdir(), `cc-browser-state-${Date.now()}.png`)
3201
+ : null;
3202
+ const state = await captureState({
3203
+ port: args.port != null ? Number(args.port) : undefined,
3204
+ tab: args.tab != null ? Number(args.tab) : undefined,
3205
+ reload: args.reload === true,
3206
+ watchMs: args.watch_ms != null ? Number(args.watch_ms) : undefined,
3207
+ includeDom: args.include_dom !== false,
3208
+ domCap: args.dom_cap != null ? Number(args.dom_cap) : 40000,
3209
+ screenshotPath,
3210
+ });
3211
+ if (!state.ok) {
3212
+ return attachDescriptor({
3213
+ error: `browser_state failed: ${state.error}`,
3214
+ });
3215
+ }
3216
+ return attachDescriptor(state);
3217
+ } catch (err) {
3218
+ return attachDescriptor({
3219
+ error: `browser_state failed: ${err.message}`,
3220
+ });
3221
+ }
3222
+ }
3223
+
3107
3224
  case "search_files": {
3108
3225
  // An explicit directory scopes the search to one root; otherwise span
3109
3226
  // cwd plus any --add-dir roots so cross-package searches find matches.
@@ -6438,6 +6555,10 @@ export function formatToolArgs(name, args) {
6438
6555
  return `"${(args.query || "").substring(0, 60)}"`;
6439
6556
  case "notify":
6440
6557
  return `${args.level || "info"}: ${(args.title || "").substring(0, 50)}`;
6558
+ case "publish_artifact":
6559
+ return `${args.kind || "other"}: ${(args.title || args.path || "").substring(0, 60)}`;
6560
+ case "browser_state":
6561
+ return `tab=${args.tab ?? 0} port=${args.port ?? 9222}${args.reload ? " reload" : ""}`;
6441
6562
  case "schedule":
6442
6563
  return args.action === "cron"
6443
6564
  ? `cron ${args.cron || ""}`.trim()
@@ -238,6 +238,12 @@ const CODING_AGENT_TOOL_CONTRACTS = Object.freeze([
238
238
  description:
239
239
  "Foreground (synchronous) timeout in milliseconds. Default 60000, max 600000. Ignored when run_in_background is true.",
240
240
  },
241
+ shell: {
242
+ type: "string",
243
+ enum: ["default", "cmd", "powershell", "pwsh"],
244
+ description:
245
+ "Shell to run the command with (optional). default = the platform shell (cmd.exe on Windows, /bin/sh elsewhere) or the configured settings shell.windowsDefault. Use powershell (Windows PowerShell 5.1) or pwsh (PowerShell 7+) when the command uses PowerShell syntax: cmdlets, object pipelines, $variables.",
246
+ },
241
247
  },
242
248
  required: ["command"],
243
249
  },
@@ -953,6 +959,105 @@ const CODING_AGENT_TOOL_CONTRACTS = Object.freeze([
953
959
  tags: ["tool:schedule", "contract:coding-agent", "tier:extension"],
954
960
  },
955
961
  },
962
+ {
963
+ name: "publish_artifact",
964
+ title: "Publish Artifact",
965
+ kind: "artifact",
966
+ tier: "extension",
967
+ description:
968
+ "Publish a finished deliverable file (Markdown report, patch, screenshot, test log, findings JSON) as a durable artifact the user can find later with `cc artifacts`. The file is copied into the user's artifact store with metadata; only the metadata enters the conversation. Use at the END of a task for its key outputs — not for intermediate scratch files.",
969
+ inputSchema: {
970
+ type: "object",
971
+ properties: {
972
+ path: {
973
+ type: "string",
974
+ description:
975
+ "Path of the file to publish (relative to cwd or absolute)",
976
+ },
977
+ title: {
978
+ type: "string",
979
+ description: "Human title for the artifact (default: the file name)",
980
+ },
981
+ kind: {
982
+ type: "string",
983
+ enum: ["report", "patch", "screenshot", "log", "data", "other"],
984
+ description: "What kind of deliverable this is (default: other)",
985
+ },
986
+ ttl_days: {
987
+ type: "number",
988
+ description:
989
+ "Days to retain before `cc artifacts clean` may remove it (default 30)",
990
+ },
991
+ },
992
+ required: ["path"],
993
+ },
994
+ ...TOOL_POLICY_METADATA.publish_artifact,
995
+ permissions: {
996
+ level: "standard",
997
+ scopes: ["filesystem:read", "artifact:write"],
998
+ },
999
+ telemetry: {
1000
+ category: "artifact",
1001
+ tags: [
1002
+ "tool:publish_artifact",
1003
+ "contract:coding-agent",
1004
+ "tier:extension",
1005
+ ],
1006
+ },
1007
+ },
1008
+ {
1009
+ name: "browser_state",
1010
+ title: "Browser State",
1011
+ kind: "browser",
1012
+ tier: "extension",
1013
+ description:
1014
+ "Capture live state from the user's real Chrome over CDP: current URL/title, open tabs, console messages and failed/error network requests observed during a short watch window, a capped DOM snapshot, and optionally a screenshot (saved to a temp file whose path is returned). Requires a debuggable Chrome — the user starts one with `cc browse chrome launch`. Console/network capture starts at attach time (CDP keeps no history) — set reload=true to observe load-time errors. Use to verify web changes in the user's logged-in browser.",
1015
+ inputSchema: {
1016
+ type: "object",
1017
+ properties: {
1018
+ port: {
1019
+ type: "number",
1020
+ description: "CDP port of the debuggable Chrome (default 9222)",
1021
+ },
1022
+ tab: {
1023
+ type: "number",
1024
+ description: "Tab index to observe (default 0)",
1025
+ },
1026
+ reload: {
1027
+ type: "boolean",
1028
+ description:
1029
+ "Reload the page first to capture load-time console/network (default false)",
1030
+ },
1031
+ watch_ms: {
1032
+ type: "number",
1033
+ description: "Observation window in milliseconds (default 3000)",
1034
+ },
1035
+ include_dom: {
1036
+ type: "boolean",
1037
+ description: "Include a capped HTML snapshot (default true)",
1038
+ },
1039
+ dom_cap: {
1040
+ type: "number",
1041
+ description: "Max HTML characters to return (default 40000)",
1042
+ },
1043
+ screenshot: {
1044
+ type: "boolean",
1045
+ description:
1046
+ "Save a screenshot to a generated temp file and return its path (default false)",
1047
+ },
1048
+ },
1049
+ required: [],
1050
+ },
1051
+ ...TOOL_POLICY_METADATA.browser_state,
1052
+ permissions: {
1053
+ level: "standard",
1054
+ scopes: ["browser:read"],
1055
+ },
1056
+ telemetry: {
1057
+ category: "browser",
1058
+ tags: ["tool:browser_state", "contract:coding-agent", "tier:extension"],
1059
+ },
1060
+ },
956
1061
  ]);
957
1062
 
958
1063
  const CODING_AGENT_MVP_TOOL_NAMES = Object.freeze(
@@ -289,6 +289,37 @@ const TOOL_POLICY_METADATA = Object.freeze({
289
289
  approvalFlow: "auto",
290
290
  isReadOnly: false,
291
291
  },
292
+ // Copies a finished deliverable (report/patch/screenshot/log) into the
293
+ // user's own ~/.chainlesschain/artifacts store with metadata. Reads a
294
+ // workspace file, writes only under the user's config dir — no third-party
295
+ // egress. Blocked in plan mode (publishing is a side effect of DONE work,
296
+ // not analysis).
297
+ publish_artifact: {
298
+ riskLevel: RISK_LEVELS.LOW,
299
+ category: TOOL_CATEGORIES.WRITE,
300
+ availableInPlanMode: false,
301
+ planModeBehavior: "blocked",
302
+ requiresPlanApproval: false,
303
+ requiresConfirmation: false,
304
+ approvalFlow: "auto",
305
+ isReadOnly: false,
306
+ },
307
+ // Attaches to the user's own debuggable Chrome over loopback CDP and
308
+ // observes one tab (console/network/DOM/optional screenshot) for a bounded
309
+ // window. Observation only — it never clicks, types, or navigates; the
310
+ // optional reload re-runs the page the user already has open, and the
311
+ // screenshot goes to a generated temp path (never an agent-chosen one), so
312
+ // the tool stays read-only and plan-mode safe.
313
+ browser_state: {
314
+ riskLevel: RISK_LEVELS.LOW,
315
+ category: TOOL_CATEGORIES.READ,
316
+ availableInPlanMode: true,
317
+ planModeBehavior: "allow",
318
+ requiresPlanApproval: false,
319
+ requiresConfirmation: false,
320
+ approvalFlow: "auto",
321
+ isReadOnly: true,
322
+ },
292
323
  });
293
324
 
294
325
  function normalizeGitCommand(command) {
@@ -181,6 +181,47 @@ export function planSnapshot(pm) {
181
181
  }
182
182
  return { active: pm.isActive(), state: pm.state || null, items, risk };
183
183
  }
184
+
185
+ const MAX_PLAN_REVIEW_SNAPSHOT_CHARS = 24000;
186
+
187
+ function planReviewFromInput(obj) {
188
+ const review =
189
+ obj && obj.review && typeof obj.review === "object" ? obj.review : null;
190
+ const snapshot =
191
+ typeof obj?.snapshot === "string"
192
+ ? obj.snapshot
193
+ : typeof review?.snapshot === "string"
194
+ ? review.snapshot
195
+ : "";
196
+ if (!snapshot.trim()) return null;
197
+ const text =
198
+ snapshot.length > MAX_PLAN_REVIEW_SNAPSHOT_CHARS
199
+ ? snapshot.slice(0, MAX_PLAN_REVIEW_SNAPSHOT_CHARS) +
200
+ `\n\n[review snapshot truncated: ${snapshot.length - MAX_PLAN_REVIEW_SNAPSHOT_CHARS} chars omitted]`
201
+ : snapshot;
202
+ return {
203
+ action: String(review?.action || obj.action || "").toLowerCase(),
204
+ reviewedAt:
205
+ typeof review?.reviewedAt === "string" ? review.reviewedAt : null,
206
+ conversationId:
207
+ typeof review?.conversationId === "string" ? review.conversationId : null,
208
+ snapshot: text,
209
+ };
210
+ }
211
+
212
+ function planReviewSystemMessage(review) {
213
+ if (!review?.snapshot) return null;
214
+ const meta = [
215
+ review.action ? `action=${review.action}` : null,
216
+ review.reviewedAt ? `reviewedAt=${review.reviewedAt}` : null,
217
+ review.conversationId ? `conversationId=${review.conversationId}` : null,
218
+ ]
219
+ .filter(Boolean)
220
+ .join(" ");
221
+ return (
222
+ "[PLAN REVIEW SNAPSHOT]" + (meta ? ` ${meta}` : "") + "\n" + review.snapshot
223
+ );
224
+ }
184
225
  import { withQuietStdout } from "./quiet-stdout.js";
185
226
 
186
227
  /**
@@ -199,7 +240,9 @@ export function parseInputEvent(line) {
199
240
  // {"type":"plan","action":"enter"|"approve"|"reject"}
200
241
  if (obj && typeof obj === "object" && obj.type === "plan") {
201
242
  const action = String(obj.action || "").toLowerCase();
202
- return action ? { plan: action } : null;
243
+ if (!action) return null;
244
+ const planReview = planReviewFromInput(obj);
245
+ return planReview ? { plan: action, planReview } : { plan: action };
203
246
  }
204
247
  // Turn interrupt (panel Stop / Claude-Code Esc parity): aborts the
205
248
  // in-flight turn without ending the conversation. {"type":"interrupt"}
@@ -1304,6 +1347,13 @@ export async function runAgentHeadlessStream(options = {}, deps = {}) {
1304
1347
  // exits plan mode. Every control answers with a `plan_update` event.
1305
1348
  if (parsed.plan) {
1306
1349
  const pm = getPlanModeManager();
1350
+ const reviewMessage =
1351
+ parsed.plan === "approve" || parsed.plan === "reject"
1352
+ ? planReviewSystemMessage(parsed.planReview)
1353
+ : null;
1354
+ if (reviewMessage) {
1355
+ messages.push({ role: "system", content: reviewMessage });
1356
+ }
1307
1357
  if (parsed.plan === "enter") {
1308
1358
  if (!pm.isActive()) {
1309
1359
  pm.enterPlanMode({ title: "Agent Plan" });
@@ -55,6 +55,10 @@ export function resolveAgentPolicy({
55
55
  fallbackModels: overrides.fallbackModels,
56
56
  pdh: overrides.pdh,
57
57
  outputStyle: overrides.outputStyle,
58
+ disableSlashCommands: overrides.disableSlashCommands === true,
59
+ // --remote-control also applies interactively: the REPL starts the
60
+ // paired-device approval bridge at startup (批26).
61
+ remoteControl: overrides.remoteControl === true,
58
62
  };
59
63
  }
60
64