chainlesschain 0.162.158 → 0.162.159

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 (201) hide show
  1. package/package.json +1 -1
  2. package/src/assets/web-panel/assets/{AIOps-BwpTJTN_.js → AIOps-ByIiF5bT.js} +1 -1
  3. package/src/assets/web-panel/assets/{ActionButton-BtQOoo3l.js → ActionButton-NlgSFZq5.js} +1 -1
  4. package/src/assets/web-panel/assets/{Analytics-Duf_rXZa.js → Analytics-D17cOUcb.js} +3 -3
  5. package/src/assets/web-panel/assets/{AppLayout-XQK3wjvB.js → AppLayout-B4mEZcbl.js} +4 -4
  6. package/src/assets/web-panel/assets/Artifacts-CASsmyQw.js +1 -0
  7. package/src/assets/web-panel/assets/{Audit-DNjOGQLh.js → Audit-DYAia2e2.js} +1 -1
  8. package/src/assets/web-panel/assets/{BackgroundAgents-FH9RAfB5.js → BackgroundAgents-C1lvYUSu.js} +1 -1
  9. package/src/assets/web-panel/assets/{Backup-ZIbrR4-U.js → Backup-B6agK6bA.js} +1 -1
  10. package/src/assets/web-panel/assets/{BaseInput-DSt3NaND.js → BaseInput-DM43R2vy.js} +1 -1
  11. package/src/assets/web-panel/assets/{Chat-CY-wvbjv.js → Chat-Dtdp3eXp.js} +5 -5
  12. package/src/assets/web-panel/assets/{ChatBubbleRenderer-BAV9V-t-.js → ChatBubbleRenderer-BL5c_OQ1.js} +1 -1
  13. package/src/assets/web-panel/assets/{Checkbox-8JVadE_B.js → Checkbox-D2xf7uZt.js} +1 -1
  14. package/src/assets/web-panel/assets/{Codegen-CN83nPYI.js → Codegen-D3j45jJL.js} +1 -1
  15. package/src/assets/web-panel/assets/{Col-xPSvqdN8.js → Col-voPJ9fUZ.js} +1 -1
  16. package/src/assets/web-panel/assets/{Community-D78cnIjG.js → Community-DEsyTESF.js} +1 -1
  17. package/src/assets/web-panel/assets/{Compact-CSw6Csbp.js → Compact-OHV_QXLD.js} +1 -1
  18. package/src/assets/web-panel/assets/{Compliance-E_HhjcYR.js → Compliance-eDTJWC-J.js} +1 -1
  19. package/src/assets/web-panel/assets/{Cowork-DybeQjmm.js → Cowork-BBvy-6d1.js} +3 -3
  20. package/src/assets/web-panel/assets/{Cron-DS0BJeLY.js → Cron-Dw2eoJxu.js} +2 -2
  21. package/src/assets/web-panel/assets/{Crosschain-BOwVgkbs.js → Crosschain-DQHms_Bh.js} +1 -1
  22. package/src/assets/web-panel/assets/{DID-DRUr9YuX.js → DID-Cyu7Id1c.js} +2 -2
  23. package/src/assets/web-panel/assets/{Dashboard-Cht-yUKW.js → Dashboard-sIDzkG92.js} +2 -2
  24. package/src/assets/web-panel/assets/{Dropdown-BLr_EJ9X.js → Dropdown-DUW3T2ey.js} +1 -1
  25. package/src/assets/web-panel/assets/{EmailListRenderer-Drexuh5W.js → EmailListRenderer-CECELXW8.js} +1 -1
  26. package/src/assets/web-panel/assets/{FamilyGuardDashboard-D9Q2Kq63.js → FamilyGuardDashboard-BIZR37ov.js} +1 -1
  27. package/src/assets/web-panel/assets/{Federation-C-Z1xI-T.js → Federation-BEZIQX84.js} +1 -1
  28. package/src/assets/web-panel/assets/{FormItemContext-DLHPMkQF.js → FormItemContext-BcdUMznL.js} +1 -1
  29. package/src/assets/web-panel/assets/{GenericCardRenderer-DxxEdn2r.js → GenericCardRenderer-DsN_fr5H.js} +1 -1
  30. package/src/assets/web-panel/assets/{Git-syATcaBt.js → Git-63LhjBx2.js} +2 -2
  31. package/src/assets/web-panel/assets/{Governance-CYe_r8Zf.js → Governance-CYtQtIcH.js} +1 -1
  32. package/src/assets/web-panel/assets/{Inference-GWxErB7k.js → Inference-BwR6UUox.js} +1 -1
  33. package/src/assets/web-panel/assets/{KnowledgeGraph-qQvVCZoi.js → KnowledgeGraph-D9lVNTUA.js} +1 -1
  34. package/src/assets/web-panel/assets/{Logs-BenAU2W8.js → Logs-C4q5h7Ut.js} +2 -2
  35. package/src/assets/web-panel/assets/{MarkdownRenderer-7agE3Or1.js → MarkdownRenderer-DexLlXfN.js} +1 -1
  36. package/src/assets/web-panel/assets/{Marketplace-D4Jn5fpD.js → Marketplace-Plksliuw.js} +1 -1
  37. package/src/assets/web-panel/assets/{McpTools-DtAwoqiQ.js → McpTools-DZUFfeuG.js} +4 -4
  38. package/src/assets/web-panel/assets/{Memory-BI_O50I8.js → Memory-NoDYt29V.js} +2 -2
  39. package/src/assets/web-panel/assets/{MobileBridge-CQWE2lqx.js → MobileBridge-CEXGTkdh.js} +1 -1
  40. package/src/assets/web-panel/assets/{MobileProjects-CVtTqgtg.js → MobileProjects-DnbJkbZo.js} +1 -1
  41. package/src/assets/web-panel/assets/{Mtc-Dnr16qsn.js → Mtc-DTAa0cwy.js} +5 -5
  42. package/src/assets/web-panel/assets/{MtcAudit-CQ5Xe4-a.js → MtcAudit-CVV7LnNS.js} +2 -2
  43. package/src/assets/web-panel/assets/{Multisig-Bjr8O-wy.js → Multisig-BqqGrjt_.js} +3 -3
  44. package/src/assets/web-panel/assets/{NLProgramming-CEe-KirU.js → NLProgramming-C4WP8DRp.js} +1 -1
  45. package/src/assets/web-panel/assets/{Notes-spKJIKF9.js → Notes-Zo4DJUVI.js} +4 -4
  46. package/src/assets/web-panel/assets/{NotificationSettings-DRp83LFG.js → NotificationSettings-DBcjRaz2.js} +1 -1
  47. package/src/assets/web-panel/assets/OrderTableRenderer-BCQel9bD.js +1 -0
  48. package/src/assets/web-panel/assets/{Organization-BlgB0IYg.js → Organization-BT9WPBOX.js} +4 -4
  49. package/src/assets/web-panel/assets/{Overflow-a_35HqZI.js → Overflow-D6b4bZ3q.js} +1 -1
  50. package/src/assets/web-panel/assets/{P2P-CSNHCNPC.js → P2P-Cw3OsYx-.js} +2 -2
  51. package/src/assets/web-panel/assets/{PdhVaultBrowser-C8M1JH-1.js → PdhVaultBrowser-C0nyiK2b.js} +5 -5
  52. package/src/assets/web-panel/assets/{Permissions-DmjJi9Ms.js → Permissions-zVBZEB_C.js} +3 -3
  53. package/src/assets/web-panel/assets/{PersonalDataHub-DfuwRyf4.js → PersonalDataHub-BOspsFew.js} +2 -2
  54. package/src/assets/web-panel/assets/{Pipeline-B2-S4ekE.js → Pipeline-CQWfZ4XH.js} +1 -1
  55. package/src/assets/web-panel/assets/{Privacy-B1dOiBLC.js → Privacy-C2oGJ_WL.js} +1 -1
  56. package/src/assets/web-panel/assets/{ProjectInit-_pNmc-DX.js → ProjectInit-BZMR4qY1.js} +2 -2
  57. package/src/assets/web-panel/assets/{ProjectSettings-DmwG2Ii1.js → ProjectSettings-DAujoP63.js} +2 -2
  58. package/src/assets/web-panel/assets/{Projects-COATmHxF.js → Projects-DvH_P2Uq.js} +1 -1
  59. package/src/assets/web-panel/assets/{Providers-C6HSjg1d.js → Providers-D_RGHM-v.js} +1 -1
  60. package/src/assets/web-panel/assets/{QrScannerModal-CrDmS7l1.js → QrScannerModal-Mf6KfL2J.js} +1 -1
  61. package/src/assets/web-panel/assets/{QuickAsk-CfKvWt-x.js → QuickAsk-5D8URoI_.js} +1 -1
  62. package/src/assets/web-panel/assets/{Recommend-DMa6Kuzv.js → Recommend-Df4kC8Gy.js} +1 -1
  63. package/src/assets/web-panel/assets/{RemoteSession-DrktUo5x.js → RemoteSession-DGi3f3vO.js} +2 -2
  64. package/src/assets/web-panel/assets/{Reputation-BTPQ1vjL.js → Reputation-jmkGFNWb.js} +1 -1
  65. package/src/assets/web-panel/assets/{Row-B6Q7QkMG.js → Row-DP6Pcu3m.js} +1 -1
  66. package/src/assets/web-panel/assets/{RssFeed-B1IJhAmX.js → RssFeed-D60hSuPO.js} +3 -3
  67. package/src/assets/web-panel/assets/{Search-xbea1kGD.js → Search-0mf1BQUK.js} +1 -1
  68. package/src/assets/web-panel/assets/{Security-7rs0cO3F.js → Security-CfZtLFYV.js} +4 -4
  69. package/src/assets/web-panel/assets/{Services-0fjixK0S.js → Services-D9zpZLWz.js} +2 -2
  70. package/src/assets/web-panel/assets/{Skeleton-B_rVq9Cu.js → Skeleton-D1_44eMD.js} +1 -1
  71. package/src/assets/web-panel/assets/{Skills-C5uM90b9.js → Skills-cVFa8zkz.js} +1 -1
  72. package/src/assets/web-panel/assets/{Sla-U4pYCIZH.js → Sla-Cinpzc3L.js} +1 -1
  73. package/src/assets/web-panel/assets/{SpeechSettings-Cdz4yTWx.js → SpeechSettings-bqdiJE_P.js} +1 -1
  74. package/src/assets/web-panel/assets/{SyncSettings-Me3aetHv.js → SyncSettings-Dgl_SLgj.js} +2 -2
  75. package/src/assets/web-panel/assets/{Tasks-Cf6aNkL_.js → Tasks-DawWAEhy.js} +1 -1
  76. package/src/assets/web-panel/assets/{Templates-CYatk4tU.js → Templates-Ca0nY651.js} +1 -1
  77. package/src/assets/web-panel/assets/{Tenant-DIMKLw7o.js → Tenant-CgaGEhLm.js} +1 -1
  78. package/src/assets/web-panel/assets/{Terminal-C_cp-uSJ.js → Terminal-D-9EnpRx.js} +2 -2
  79. package/src/assets/web-panel/assets/{TimelineRenderer-cUPVNADp.js → TimelineRenderer-CSOxDHZ6.js} +1 -1
  80. package/src/assets/web-panel/assets/{Tokens-CiaEIXVf.js → Tokens-BpwLZpL3.js} +1 -1
  81. package/src/assets/web-panel/assets/{Trigger-DB1WU5lQ.js → Trigger-DgowwBK5.js} +1 -1
  82. package/src/assets/web-panel/assets/{Trust-Cfht9Ijy.js → Trust-BZutapjE.js} +1 -1
  83. package/src/assets/web-panel/assets/{UkeySign-K3P4Vt9S.js → UkeySign-QZPt8_Sp.js} +1 -1
  84. package/src/assets/web-panel/assets/{VideoEditing-DWjhjXRh.js → VideoEditing-DlQx5UDJ.js} +1 -1
  85. package/src/assets/web-panel/assets/{Wallet-D9-Q1Kan.js → Wallet-ZfdTgtWA.js} +4 -4
  86. package/src/assets/web-panel/assets/{WebAuthn-BDszy-oV.js → WebAuthn-CAp0y_Cr.js} +5 -5
  87. package/src/assets/web-panel/assets/{WorkflowEditor-DIhij4rA.js → WorkflowEditor-BVITZEmr.js} +1 -1
  88. package/src/assets/web-panel/assets/{chat-D7VCiKbh.js → chat-CC2tPaNg.js} +1 -1
  89. package/src/assets/web-panel/assets/{colors-BvOd9pVn.js → colors-31brc9CA.js} +1 -1
  90. package/src/assets/web-panel/assets/{compact-item-DoqXBiAO.js → compact-item-DLMkC9C-.js} +1 -1
  91. package/src/assets/web-panel/assets/{createContext-CrKRzUtp.js → createContext-BMtveI6F.js} +1 -1
  92. package/src/assets/web-panel/assets/devWarning-CB8pPOtd.js +1 -0
  93. package/src/assets/web-panel/assets/{hasIn-BWAev4WC.js → hasIn-DbD6WHW2.js} +1 -1
  94. package/src/assets/web-panel/assets/{index-B30NvR21.js → index-3oBsXZmF.js} +1 -1
  95. package/src/assets/web-panel/assets/{index-Cx7M7PSF.js → index-ALvoNTRF.js} +1 -1
  96. package/src/assets/web-panel/assets/{index-BHOcVYrx.js → index-AaIKG60c.js} +1 -1
  97. package/src/assets/web-panel/assets/{index-BXyl8O23.js → index-BFRLmCR3.js} +1 -1
  98. package/src/assets/web-panel/assets/{index-YGDy7nNq.js → index-BKAB6-6V.js} +1 -1
  99. package/src/assets/web-panel/assets/{index-BoYrS_L2.js → index-BOM-VFlv.js} +1 -1
  100. package/src/assets/web-panel/assets/{index-Bq_ctRpz.js → index-BPpS-lGb.js} +1 -1
  101. package/src/assets/web-panel/assets/{index-CDGLdn12.js → index-Bbc2JeQG.js} +1 -1
  102. package/src/assets/web-panel/assets/index-BhXiaiev.js +1 -0
  103. package/src/assets/web-panel/assets/{index-ISvPZ05r.js → index-BiRQUoyE.js} +1 -1
  104. package/src/assets/web-panel/assets/{index-B0vGEbJT.js → index-BnFLzejT.js} +1 -1
  105. package/src/assets/web-panel/assets/{index-Bj7EgmOp.js → index-BoO4WEJB.js} +1 -1
  106. package/src/assets/web-panel/assets/{index-BZGk8AbZ.js → index-C-cGlCDW.js} +1 -1
  107. package/src/assets/web-panel/assets/{index-BkgnGX7v.js → index-C1PfeI88.js} +1 -1
  108. package/src/assets/web-panel/assets/{index-HwHdTusY.js → index-C21pdBqs.js} +1 -1
  109. package/src/assets/web-panel/assets/{index-Bf9YlbDn.js → index-C7LHNw_N.js} +1 -1
  110. package/src/assets/web-panel/assets/{index-oY45XrkG.js → index-CEXa-4zd.js} +1 -1
  111. package/src/assets/web-panel/assets/{index-KR1-Em9C.js → index-CEwjBA9q.js} +1 -1
  112. package/src/assets/web-panel/assets/{index-C5kRmj3P.js → index-CFXKbOek.js} +1 -1
  113. package/src/assets/web-panel/assets/{index-BN9m4Ux2.js → index-CJhREOlG.js} +1 -1
  114. package/src/assets/web-panel/assets/{index-PDxGjKRu.js → index-COHHQ9cU.js} +1 -1
  115. package/src/assets/web-panel/assets/{index-D23L1V76.js → index-CUmLMFsy.js} +1 -1
  116. package/src/assets/web-panel/assets/{index-zH-tuuGZ.js → index-CWgfipcC.js} +1 -1
  117. package/src/assets/web-panel/assets/{index-CmJ6xKBO.js → index-C_eUKGYP.js} +1 -1
  118. package/src/assets/web-panel/assets/{index-CEPHMSih.js → index-CcVszn2b.js} +1 -1
  119. package/src/assets/web-panel/assets/{index-DV-Rfmfx.js → index-Cod97Urs.js} +3 -3
  120. package/src/assets/web-panel/assets/{index-BWWsaimP.js → index-Cwbqh0KZ.js} +1 -1
  121. package/src/assets/web-panel/assets/{index-8g5TJCZ3.js → index-D67kIagl.js} +1 -1
  122. package/src/assets/web-panel/assets/{index-CEXfKSiI.js → index-D7cV0tu0.js} +1 -1
  123. package/src/assets/web-panel/assets/{index-CoD31Ndm.js → index-DJvxFyIz.js} +1 -1
  124. package/src/assets/web-panel/assets/{index-CSb-tnIo.js → index-DZl-UXB6.js} +1 -1
  125. package/src/assets/web-panel/assets/{index-DvDOpsJB.js → index-Dm0byjm1.js} +1 -1
  126. package/src/assets/web-panel/assets/{index-DlGu7As6.js → index-DndNnYc0.js} +1 -1
  127. package/src/assets/web-panel/assets/{index-gmxqu5Kk.js → index-WQUO2vvK.js} +1 -1
  128. package/src/assets/web-panel/assets/index-WpaYaE3D.js +1 -0
  129. package/src/assets/web-panel/assets/{index-ww1QtLnX.js → index-bdaJwTXJ.js} +1 -1
  130. package/src/assets/web-panel/assets/{index-JzUmJMvn.js → index-ep6JchNG.js} +1 -1
  131. package/src/assets/web-panel/assets/{index-BCyP071m.js → index-jG6h22Qg.js} +1 -1
  132. package/src/assets/web-panel/assets/{index-1poMpMU0.js → index-ntVQqSML.js} +1 -1
  133. package/src/assets/web-panel/assets/{initDefaultProps-XW-OR3mL.js → initDefaultProps-D5UoBRhN.js} +1 -1
  134. package/src/assets/web-panel/assets/{motion-CeuEHh20.js → motion-DltOw0CR.js} +1 -1
  135. package/src/assets/web-panel/assets/{move-Dva79Keg.js → move-_oPkk5zj.js} +1 -1
  136. package/src/assets/web-panel/assets/{mtc-parser-C0B6bD2j.js → mtc-parser-EES8ROCS.js} +1 -1
  137. package/src/assets/web-panel/assets/{omit-AkZvO-a2.js → omit-B-_39x_M.js} +1 -1
  138. package/src/assets/web-panel/assets/{pickAttrs-B4r55qGc.js → pickAttrs-l6D0yJHz.js} +1 -1
  139. package/src/assets/web-panel/assets/{placementArrow-BLH-z7YE.js → placementArrow-DIeUK31L.js} +1 -1
  140. package/src/assets/web-panel/assets/{responsiveObserve-ksEGZ2Wf.js → responsiveObserve-dG4EzUQo.js} +1 -1
  141. package/src/assets/web-panel/assets/{slide-Cth4OD1Z.js → slide-DWFzYGdH.js} +1 -1
  142. package/src/assets/web-panel/assets/{statusUtils-9S83ezxw.js → statusUtils-CECdTA5H.js} +1 -1
  143. package/src/assets/web-panel/assets/{styleChecker-7yQmQGZ4.js → styleChecker-D6kP-mMK.js} +1 -1
  144. package/src/assets/web-panel/assets/{useFlexGapSupport-DpGnMmPc.js → useFlexGapSupport-BWfna_rd.js} +1 -1
  145. package/src/assets/web-panel/assets/{useFs-C4zDZIXH.js → useFs-Bb-j6rzo.js} +1 -1
  146. package/src/assets/web-panel/assets/{usePersonalDataHub-CoLICdUZ.js → usePersonalDataHub-D5OIsYM4.js} +1 -1
  147. package/src/assets/web-panel/assets/{vnode-CKyH-35X.js → vnode-DNpwzFTM.js} +1 -1
  148. package/src/assets/web-panel/assets/{zoom-DRDGit17.js → zoom-vNLpFQBW.js} +1 -1
  149. package/src/assets/web-panel/index.html +1 -1
  150. package/src/command-manifest.json +17 -3
  151. package/src/commands/agent.js +7 -0
  152. package/src/commands/background-session.js +143 -3
  153. package/src/commands/browse.js +86 -0
  154. package/src/commands/cloud.js +232 -0
  155. package/src/commands/doctor.js +61 -6
  156. package/src/commands/mcp.js +61 -0
  157. package/src/commands/plugin.js +63 -1
  158. package/src/commands/routine.js +367 -0
  159. package/src/commands/session.js +326 -0
  160. package/src/data/changelog.json +7 -2
  161. package/src/harness/jsonl-session-store.js +82 -2
  162. package/src/harness/session-index.js +295 -0
  163. package/src/harness/session-mirror.js +142 -0
  164. package/src/harness/transcript-integrity.js +182 -0
  165. package/src/index.js +8 -0
  166. package/src/lib/background-agent-supervisor.js +126 -2
  167. package/src/lib/channels/channel-manager.js +126 -0
  168. package/src/lib/channels/telegram-channel.js +112 -0
  169. package/src/lib/channels/webhook-channel.js +140 -0
  170. package/src/lib/chrome-connector.js +386 -0
  171. package/src/lib/cloud/bundle.js +110 -0
  172. package/src/lib/cloud/cloud-client.js +152 -0
  173. package/src/lib/computer-use/authorization.js +173 -0
  174. package/src/lib/computer-use/computer-use-server.js +276 -0
  175. package/src/lib/computer-use/control-backend.js +150 -0
  176. package/src/lib/doctor-checkup.js +652 -0
  177. package/src/lib/jsonl-session-store.js +4 -0
  178. package/src/lib/lsp/lsp-server-registry.js +48 -21
  179. package/src/lib/plugin-runtime/governance.js +174 -0
  180. package/src/lib/pr-link-ledger.js +221 -0
  181. package/src/lib/routine-store.js +352 -0
  182. package/src/lib/session-store-guard.js +40 -0
  183. package/src/lib/session-usage.js +318 -2
  184. package/src/lib/sub-agent-context.js +23 -0
  185. package/src/repl/agent-repl.js +361 -3
  186. package/src/repl/bg-dashboard.js +355 -0
  187. package/src/repl/fast-mode.js +103 -0
  188. package/src/repl/fullscreen-tui.js +197 -0
  189. package/src/repl/slash-command-registry.js +11 -3
  190. package/src/repl/voice-dictation.js +132 -0
  191. package/src/runtime/agent-core.js +242 -1
  192. package/src/runtime/coding-agent-contract-shared.cjs +88 -0
  193. package/src/runtime/coding-agent-policy.cjs +17 -0
  194. package/src/runtime/headless-runner.js +71 -7
  195. package/src/runtime/headless-stream.js +14 -8
  196. package/src/runtime/policies/agent-policy.js +3 -0
  197. package/src/assets/web-panel/assets/Artifacts-DQvJoQ6E.js +0 -1
  198. package/src/assets/web-panel/assets/OrderTableRenderer-h1A0XB2a.js +0 -1
  199. package/src/assets/web-panel/assets/devWarning-nDB--2xr.js +0 -1
  200. package/src/assets/web-panel/assets/index-B1_pRTtB.js +0 -1
  201. package/src/assets/web-panel/assets/index-TMM9FuW-.js +0 -1
@@ -0,0 +1,173 @@
1
+ /**
2
+ * Computer-use authorization (gap-2026-07-11 P1#6) — the trust core shared by
3
+ * the computer-use MCP server. GUI control is the highest-privilege surface an
4
+ * agent can hold (it can click "OK" on anything), so it is:
5
+ * - OFF unless explicitly enabled (`cc mcp computer-use` / --enable);
6
+ * - capability-gated: each verb (screenshot/click/type/scroll/clipboard/
7
+ * app-launch/window-list) can be individually withheld;
8
+ * - per-app scoped: an allowlist confines control to named apps, and a
9
+ * high-risk denylist (terminals, IDEs, file managers, system settings,
10
+ * password managers) always requires confirmation even when allowed.
11
+ *
12
+ * Pure + dependency-free so it is exhaustively unit-tested; the server and the
13
+ * OS backend consume its verdicts.
14
+ */
15
+
16
+ export const COMPUTER_USE_CAPABILITIES = [
17
+ "screenshot",
18
+ "window_list",
19
+ "app_launch",
20
+ "click",
21
+ "type",
22
+ "scroll",
23
+ "clipboard_read",
24
+ "clipboard_write",
25
+ ];
26
+
27
+ // Read-only observation is lower risk than input injection; the tool-priority
28
+ // guidance (MCP/API > shell > browser action > computer use) lives here so the
29
+ // server can surface it and the model prefers cheaper, auditable paths.
30
+ export const TOOL_PRIORITY = [
31
+ "mcp/api",
32
+ "shell",
33
+ "browser action",
34
+ "computer use",
35
+ ];
36
+
37
+ // Apps where an unattended click/type is disproportionately dangerous — a
38
+ // terminal runs commands, a password manager reveals secrets, system settings
39
+ // change the machine. Matched case-insensitively as a substring of the app /
40
+ // window identifier. Confirmation is forced even inside the allowlist.
41
+ export const HIGH_RISK_APP_PATTERNS = [
42
+ "terminal",
43
+ "powershell",
44
+ "cmd.exe",
45
+ "conhost",
46
+ "windows terminal",
47
+ "iterm",
48
+ "code", // VS Code / VSCodium
49
+ "jetbrains",
50
+ "intellij",
51
+ "explorer.exe",
52
+ "finder",
53
+ "nautilus",
54
+ "settings",
55
+ "control panel",
56
+ "regedit",
57
+ "keychain",
58
+ "1password",
59
+ "bitwarden",
60
+ "lastpass",
61
+ "keepass",
62
+ ];
63
+
64
+ /**
65
+ * @param {object} config computer-use config subtree
66
+ * { enabled, capabilities?: string[], appAllowlist?: string[],
67
+ * highRiskPatterns?: string[], confirmAll?: boolean }
68
+ */
69
+ export function createAuthorizer(config = {}) {
70
+ const enabled = config.enabled === true;
71
+ const caps = new Set(
72
+ Array.isArray(config.capabilities) && config.capabilities.length > 0
73
+ ? config.capabilities
74
+ : COMPUTER_USE_CAPABILITIES,
75
+ );
76
+ const allowlist = Array.isArray(config.appAllowlist)
77
+ ? config.appAllowlist.map((a) => String(a).toLowerCase())
78
+ : null; // null = no app restriction (any app), [] = deny every app
79
+ const highRisk = (
80
+ Array.isArray(config.highRiskPatterns)
81
+ ? config.highRiskPatterns
82
+ : HIGH_RISK_APP_PATTERNS
83
+ ).map((p) => String(p).toLowerCase());
84
+ const confirmAll = config.confirmAll === true;
85
+
86
+ const INPUT_VERBS = new Set([
87
+ "click",
88
+ "type",
89
+ "scroll",
90
+ "app_launch",
91
+ "clipboard_write",
92
+ ]);
93
+
94
+ function isHighRiskApp(app) {
95
+ const a = String(app || "").toLowerCase();
96
+ if (!a) return false;
97
+ return highRisk.some((p) => a.includes(p));
98
+ }
99
+
100
+ /**
101
+ * @returns {{ allowed:boolean, requiresConfirmation:boolean, reason?:string }}
102
+ */
103
+ function authorize(capability, context = {}) {
104
+ if (!enabled) {
105
+ return {
106
+ allowed: false,
107
+ requiresConfirmation: false,
108
+ reason:
109
+ "computer-use is disabled — enable it explicitly (cc mcp computer-use --enable)",
110
+ };
111
+ }
112
+ if (!COMPUTER_USE_CAPABILITIES.includes(capability)) {
113
+ return {
114
+ allowed: false,
115
+ requiresConfirmation: false,
116
+ reason: `unknown capability: ${capability}`,
117
+ };
118
+ }
119
+ if (!caps.has(capability)) {
120
+ return {
121
+ allowed: false,
122
+ requiresConfirmation: false,
123
+ reason: `capability "${capability}" is not enabled for this session`,
124
+ };
125
+ }
126
+
127
+ const app = context.app || context.window || null;
128
+
129
+ // App allowlist applies to app-targeted verbs (input + launch). Pure
130
+ // observation (screenshot / window_list / clipboard_read) is not app-scoped.
131
+ if (allowlist && (INPUT_VERBS.has(capability) || app)) {
132
+ const target = String(app || context.target || "").toLowerCase();
133
+ if (!target) {
134
+ return {
135
+ allowed: false,
136
+ requiresConfirmation: false,
137
+ reason: `capability "${capability}" needs a target app while an allowlist is configured`,
138
+ };
139
+ }
140
+ const ok = allowlist.some(
141
+ (a) => target.includes(a) || a.includes(target),
142
+ );
143
+ if (!ok) {
144
+ return {
145
+ allowed: false,
146
+ requiresConfirmation: false,
147
+ reason: `app "${app || context.target}" is not in the computer-use allowlist`,
148
+ };
149
+ }
150
+ }
151
+
152
+ // High-risk apps and input verbs (or confirmAll) require confirmation.
153
+ const highRiskApp = isHighRiskApp(app);
154
+ const requiresConfirmation =
155
+ confirmAll || highRiskApp || INPUT_VERBS.has(capability);
156
+ return {
157
+ allowed: true,
158
+ requiresConfirmation,
159
+ reason: highRiskApp
160
+ ? `high-risk app (${app}) — confirmation required`
161
+ : requiresConfirmation
162
+ ? `${capability} injects input — confirmation required`
163
+ : undefined,
164
+ };
165
+ }
166
+
167
+ return {
168
+ enabled,
169
+ capabilities: [...caps],
170
+ authorize,
171
+ isHighRiskApp,
172
+ };
173
+ }
@@ -0,0 +1,276 @@
1
+ /**
2
+ * Computer-use MCP server (gap-2026-07-11 P1#6) — exposes GUI control verbs to
3
+ * an MCP client, gated by the authorization core (authorization.js) and driven
4
+ * by an OS backend (control-backend.js). Streamable-HTTP MCP, same transport
5
+ * shape as `cc mcp serve`: POST JSON-RPC → application/json.
6
+ *
7
+ * Every tools/call runs through the authorizer first. A verb the authorizer
8
+ * marks `requiresConfirmation` is refused unless the caller supplied
9
+ * `confirm: true` in the tool args (the MCP client's approval surface / the
10
+ * agent's confirm flow owns the human check) — an unattended agent can never
11
+ * silently click a high-risk app.
12
+ */
13
+
14
+ import http from "node:http";
15
+ import { join } from "node:path";
16
+ import { tmpdir } from "node:os";
17
+ import { randomBytes, timingSafeEqual } from "node:crypto";
18
+ import {
19
+ createAuthorizer,
20
+ COMPUTER_USE_CAPABILITIES,
21
+ TOOL_PRIORITY,
22
+ } from "./authorization.js";
23
+ import { createWindowsBackend } from "./control-backend.js";
24
+
25
+ const rpcResult = (id, result) =>
26
+ JSON.stringify({ jsonrpc: "2.0", id, result });
27
+ const rpcError = (id, code, message) =>
28
+ JSON.stringify({ jsonrpc: "2.0", id, error: { code, message } });
29
+ const ok = (data) => ({
30
+ content: [{ type: "text", text: JSON.stringify(data) }],
31
+ });
32
+ const fail = (msg) => ({
33
+ content: [{ type: "text", text: msg }],
34
+ isError: true,
35
+ });
36
+
37
+ function bearerMatches(authHeader, token) {
38
+ const m = /^Bearer\s+(.+)$/.exec(String(authHeader || ""));
39
+ if (!m) return false;
40
+ const a = Buffer.from(m[1]);
41
+ const b = Buffer.from(token);
42
+ return a.length === b.length && timingSafeEqual(a, b);
43
+ }
44
+
45
+ const TOOL_SCHEMAS = {
46
+ computer_screenshot: {
47
+ description: "Capture the screen to a PNG (read-only observation).",
48
+ inputSchema: { type: "object", properties: {} },
49
+ },
50
+ computer_window_list: {
51
+ description: "List visible windows (pid, app, title).",
52
+ inputSchema: { type: "object", properties: {} },
53
+ },
54
+ computer_clipboard_read: {
55
+ description: "Read the system clipboard text.",
56
+ inputSchema: { type: "object", properties: {} },
57
+ },
58
+ computer_clipboard_write: {
59
+ description: "Write text to the system clipboard.",
60
+ inputSchema: {
61
+ type: "object",
62
+ properties: { text: { type: "string" } },
63
+ required: ["text"],
64
+ },
65
+ },
66
+ computer_click: {
67
+ description:
68
+ "Click at screen coordinates (input injection; requires confirm:true for high-risk apps).",
69
+ inputSchema: {
70
+ type: "object",
71
+ properties: {
72
+ x: { type: "number" },
73
+ y: { type: "number" },
74
+ app: { type: "string" },
75
+ confirm: { type: "boolean" },
76
+ },
77
+ required: ["x", "y"],
78
+ },
79
+ },
80
+ computer_type: {
81
+ description: "Type text into the focused window (input injection).",
82
+ inputSchema: {
83
+ type: "object",
84
+ properties: {
85
+ text: { type: "string" },
86
+ app: { type: "string" },
87
+ confirm: { type: "boolean" },
88
+ },
89
+ required: ["text"],
90
+ },
91
+ },
92
+ computer_scroll: {
93
+ description: "Scroll by N notches (positive up, negative down).",
94
+ inputSchema: {
95
+ type: "object",
96
+ properties: {
97
+ amount: { type: "number" },
98
+ app: { type: "string" },
99
+ confirm: { type: "boolean" },
100
+ },
101
+ required: ["amount"],
102
+ },
103
+ },
104
+ computer_app_launch: {
105
+ description: "Launch an application by path/name.",
106
+ inputSchema: {
107
+ type: "object",
108
+ properties: {
109
+ app: { type: "string" },
110
+ args: { type: "array", items: { type: "string" } },
111
+ confirm: { type: "boolean" },
112
+ },
113
+ required: ["app"],
114
+ },
115
+ },
116
+ };
117
+
118
+ const TOOL_CAPABILITY = {
119
+ computer_screenshot: "screenshot",
120
+ computer_window_list: "window_list",
121
+ computer_clipboard_read: "clipboard_read",
122
+ computer_clipboard_write: "clipboard_write",
123
+ computer_click: "click",
124
+ computer_type: "type",
125
+ computer_scroll: "scroll",
126
+ computer_app_launch: "app_launch",
127
+ };
128
+
129
+ /**
130
+ * Execute one tool call through authorize → confirm-gate → backend.
131
+ * Pure orchestration (no HTTP) so it is unit-tested directly.
132
+ */
133
+ export function handleToolCall(name, args = {}, { authorizer, backend }) {
134
+ const capability = TOOL_CAPABILITY[name];
135
+ if (!capability) return fail(`unknown tool: ${name}`);
136
+
137
+ const context = { app: args.app || null, target: args.app || null };
138
+ const verdict = authorizer.authorize(capability, context);
139
+ if (!verdict.allowed) return fail(`[computer-use denied] ${verdict.reason}`);
140
+ if (verdict.requiresConfirmation && args.confirm !== true) {
141
+ return fail(
142
+ `[computer-use needs confirmation] ${verdict.reason || capability} — re-call with confirm:true after the user approves`,
143
+ );
144
+ }
145
+
146
+ let result;
147
+ switch (name) {
148
+ case "computer_screenshot":
149
+ result = backend.screenshot(
150
+ args.path ||
151
+ join(tmpdir(), `cc-screenshot-${randomBytes(4).toString("hex")}.png`),
152
+ );
153
+ break;
154
+ case "computer_window_list":
155
+ result = backend.windowList();
156
+ break;
157
+ case "computer_clipboard_read":
158
+ result = backend.clipboardRead();
159
+ break;
160
+ case "computer_clipboard_write":
161
+ result = backend.clipboardWrite(args.text);
162
+ break;
163
+ case "computer_click":
164
+ result = backend.click(args.x, args.y);
165
+ break;
166
+ case "computer_type":
167
+ result = backend.type(args.text);
168
+ break;
169
+ case "computer_scroll":
170
+ result = backend.scroll(args.amount);
171
+ break;
172
+ case "computer_app_launch":
173
+ result = backend.appLaunch(args.app, args.args || []);
174
+ break;
175
+ default:
176
+ return fail(`unhandled tool: ${name}`);
177
+ }
178
+ return result.ok ? ok(result) : fail(result.error || `${name} failed`);
179
+ }
180
+
181
+ export function availableTools(authorizer) {
182
+ return Object.entries(TOOL_SCHEMAS)
183
+ .filter(([name]) => authorizer.capabilities.includes(TOOL_CAPABILITY[name]))
184
+ .map(([name, t]) => ({
185
+ name,
186
+ description: t.description,
187
+ inputSchema: t.inputSchema,
188
+ }));
189
+ }
190
+
191
+ export function startComputerUseServer(opts = {}) {
192
+ const authorizer = createAuthorizer({
193
+ enabled: true, // starting the server IS the explicit opt-in
194
+ ...opts.config,
195
+ });
196
+ const backend = opts.backend || createWindowsBackend();
197
+ const token =
198
+ opts.token === false ? null : opts.token || randomBytes(16).toString("hex");
199
+
200
+ const server = http.createServer((req, res) => {
201
+ const send = (status, body) => {
202
+ res.writeHead(status, { "Content-Type": "application/json" });
203
+ res.end(body);
204
+ };
205
+ if (req.method !== "POST")
206
+ return send(405, rpcError(null, -32600, "POST only"));
207
+ if (token && !bearerMatches(req.headers.authorization, token)) {
208
+ return send(401, rpcError(null, -32001, "unauthorized"));
209
+ }
210
+ let raw = "";
211
+ let bytes = 0;
212
+ let aborted = false;
213
+ req.on("data", (c) => {
214
+ if (aborted) return;
215
+ bytes += c.length;
216
+ if (bytes > 1024 * 1024) {
217
+ aborted = true;
218
+ send(413, rpcError(null, -32600, "request too large"));
219
+ req.destroy();
220
+ } else raw += c;
221
+ });
222
+ req.on("end", () => {
223
+ if (aborted) return;
224
+ let msg;
225
+ try {
226
+ msg = JSON.parse(raw);
227
+ } catch {
228
+ return send(400, rpcError(null, -32700, "parse error"));
229
+ }
230
+ const { id, method, params } = msg || {};
231
+ if (method === "initialize") {
232
+ return send(
233
+ 200,
234
+ rpcResult(id, {
235
+ protocolVersion: params?.protocolVersion || "2025-03-26",
236
+ capabilities: { tools: {} },
237
+ serverInfo: { name: "cc-computer-use", version: "1.0.0" },
238
+ instructions: `Tool priority (prefer the cheapest, most auditable): ${TOOL_PRIORITY.join(" > ")}. Input verbs need confirm:true.`,
239
+ }),
240
+ );
241
+ }
242
+ if (method === "notifications/initialized") {
243
+ res.writeHead(202);
244
+ return res.end();
245
+ }
246
+ if (method === "tools/list") {
247
+ return send(200, rpcResult(id, { tools: availableTools(authorizer) }));
248
+ }
249
+ if (method === "tools/call") {
250
+ const result = handleToolCall(params?.name, params?.arguments || {}, {
251
+ authorizer,
252
+ backend,
253
+ });
254
+ return send(200, rpcResult(id, result));
255
+ }
256
+ return send(200, rpcError(id, -32601, `unknown method: ${method}`));
257
+ });
258
+ });
259
+
260
+ return new Promise((resolve, reject) => {
261
+ server.once("error", reject);
262
+ server.listen(opts.port || 0, "127.0.0.1", () => {
263
+ const port = server.address().port;
264
+ resolve({
265
+ server,
266
+ port,
267
+ token,
268
+ url: `http://127.0.0.1:${port}`,
269
+ capabilities: authorizer.capabilities,
270
+ stop: () => new Promise((r) => server.close(() => r())),
271
+ });
272
+ });
273
+ });
274
+ }
275
+
276
+ export { COMPUTER_USE_CAPABILITIES };
@@ -0,0 +1,150 @@
1
+ /**
2
+ * Computer-use OS control backend. The default backend drives Windows via
3
+ * PowerShell + .NET (System.Windows.Forms / System.Drawing) with zero npm
4
+ * deps; every primitive is a small injectable `runPowerShell` call so the
5
+ * server logic is testable without a display. POSIX support is a documented
6
+ * gap (returns an unsupported error) — the Windows-first host matches the
7
+ * project's U-Key/hardware posture.
8
+ */
9
+
10
+ import { spawnSync } from "node:child_process";
11
+
12
+ export const _deps = { spawnSync, platform: () => process.platform };
13
+
14
+ /** Run a PowerShell script, returning { ok, stdout, stderr }. */
15
+ function runPowerShell(script, deps = _deps) {
16
+ const res = deps.spawnSync(
17
+ "powershell.exe",
18
+ ["-NoProfile", "-NonInteractive", "-Command", script],
19
+ { encoding: "utf-8", windowsHide: true, maxBuffer: 16 * 1024 * 1024 },
20
+ );
21
+ if (res.error) return { ok: false, stdout: "", stderr: res.error.message };
22
+ return {
23
+ ok: res.status === 0,
24
+ stdout: String(res.stdout || ""),
25
+ stderr: String(res.stderr || ""),
26
+ };
27
+ }
28
+
29
+ // PowerShell script fragments kept as constants so tests can assert the backend
30
+ // dispatches the right one per verb without a real display.
31
+ export const PS = {
32
+ screenshot: (outPath) => `
33
+ Add-Type -AssemblyName System.Windows.Forms,System.Drawing
34
+ $b = [System.Windows.Forms.SystemInformation]::VirtualScreen
35
+ $bmp = New-Object System.Drawing.Bitmap($b.Width, $b.Height)
36
+ $g = [System.Drawing.Graphics]::FromImage($bmp)
37
+ $g.CopyFromScreen($b.X, $b.Y, 0, 0, $bmp.Size)
38
+ $bmp.Save(${JSON.stringify(outPath)}, [System.Drawing.Imaging.ImageFormat]::Png)
39
+ Write-Output "$($b.Width)x$($b.Height)"`,
40
+ windowList: `
41
+ Get-Process | Where-Object { $_.MainWindowTitle } |
42
+ Select-Object -Property Id, ProcessName, MainWindowTitle |
43
+ ConvertTo-Json -Compress`,
44
+ clipboardRead: `Get-Clipboard -Raw`,
45
+ clipboardWrite: (text) =>
46
+ `Set-Clipboard -Value ${JSON.stringify(String(text))}`,
47
+ click: (x, y) => `
48
+ Add-Type -AssemblyName System.Windows.Forms
49
+ [System.Windows.Forms.Cursor]::Position = New-Object System.Drawing.Point(${Number(x)}, ${Number(y)})
50
+ Add-Type -MemberDefinition '[DllImport("user32.dll")] public static extern void mouse_event(int f,int dx,int dy,int d,int e);' -Name U -Namespace W
51
+ [W.U]::mouse_event(0x02,0,0,0,0); [W.U]::mouse_event(0x04,0,0,0,0)`,
52
+ type: (text) => `
53
+ Add-Type -AssemblyName System.Windows.Forms
54
+ [System.Windows.Forms.SendKeys]::SendWait(${JSON.stringify(escapeSendKeys(String(text)))})`,
55
+ scroll: (amount) => `
56
+ Add-Type -MemberDefinition '[DllImport("user32.dll")] public static extern void mouse_event(int f,int dx,int dy,int d,int e);' -Name S -Namespace W
57
+ [W.S]::mouse_event(0x0800,0,0,${Number(amount) * 120},0)`,
58
+ };
59
+
60
+ /** SendKeys treats +^%~(){}[] as control chars — escape literals in braces. */
61
+ export function escapeSendKeys(text) {
62
+ return text.replace(/[+^%~(){}\[\]]/g, (ch) => `{${ch}}`);
63
+ }
64
+
65
+ export function createWindowsBackend(deps = _deps) {
66
+ const ps = (script) => runPowerShell(script, deps);
67
+ const unsupported = (verb) => ({
68
+ ok: false,
69
+ error: `computer-use ${verb} is only implemented on Windows in this build`,
70
+ });
71
+ const guard = (verb, fn) =>
72
+ deps.platform() === "win32" ? fn() : unsupported(verb);
73
+
74
+ return {
75
+ platform: deps.platform(),
76
+ screenshot(outPath) {
77
+ return guard("screenshot", () => {
78
+ const r = ps(PS.screenshot(outPath));
79
+ return r.ok
80
+ ? { ok: true, path: outPath, size: r.stdout.trim() }
81
+ : { ok: false, error: r.stderr || "screenshot failed" };
82
+ });
83
+ },
84
+ windowList() {
85
+ return guard("window_list", () => {
86
+ const r = ps(PS.windowList);
87
+ if (!r.ok)
88
+ return { ok: false, error: r.stderr || "window list failed" };
89
+ try {
90
+ const parsed = r.stdout.trim() ? JSON.parse(r.stdout) : [];
91
+ const rows = Array.isArray(parsed) ? parsed : [parsed];
92
+ return {
93
+ ok: true,
94
+ windows: rows.map((w) => ({
95
+ pid: w.Id,
96
+ app: w.ProcessName,
97
+ title: w.MainWindowTitle,
98
+ })),
99
+ };
100
+ } catch {
101
+ return { ok: false, error: "could not parse window list" };
102
+ }
103
+ });
104
+ },
105
+ clipboardRead() {
106
+ return guard("clipboard_read", () => {
107
+ const r = ps(PS.clipboardRead);
108
+ return r.ok
109
+ ? { ok: true, text: r.stdout.replace(/\r?\n$/, "") }
110
+ : { ok: false, error: r.stderr || "clipboard read failed" };
111
+ });
112
+ },
113
+ clipboardWrite(text) {
114
+ return guard("clipboard_write", () => {
115
+ const r = ps(PS.clipboardWrite(text));
116
+ return r.ok ? { ok: true } : { ok: false, error: r.stderr };
117
+ });
118
+ },
119
+ click(x, y) {
120
+ return guard("click", () => {
121
+ const r = ps(PS.click(x, y));
122
+ return r.ok ? { ok: true, x, y } : { ok: false, error: r.stderr };
123
+ });
124
+ },
125
+ type(text) {
126
+ return guard("type", () => {
127
+ const r = ps(PS.type(text));
128
+ return r.ok ? { ok: true } : { ok: false, error: r.stderr };
129
+ });
130
+ },
131
+ scroll(amount) {
132
+ return guard("scroll", () => {
133
+ const r = ps(PS.scroll(amount));
134
+ return r.ok ? { ok: true } : { ok: false, error: r.stderr };
135
+ });
136
+ },
137
+ appLaunch(app, args = []) {
138
+ return guard("app_launch", () => {
139
+ const res = deps.spawnSync(app, args, {
140
+ windowsHide: false,
141
+ detached: true,
142
+ stdio: "ignore",
143
+ });
144
+ return res.error
145
+ ? { ok: false, error: res.error.message }
146
+ : { ok: true, app };
147
+ });
148
+ },
149
+ };
150
+ }