chainlesschain 0.162.174 → 0.162.176

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 (281) hide show
  1. package/package.json +10 -10
  2. package/src/assets/web-panel/assets/{AIOps-Dcl-X1Hw.js → AIOps-BDLu5Elz.js} +1 -1
  3. package/src/assets/web-panel/assets/{ActionButton-ClMh9HzT.js → ActionButton-DH5us-CK.js} +1 -1
  4. package/src/assets/web-panel/assets/{Analytics-DPj0rSQ5.js → Analytics-BZ0u8IFG.js} +3 -3
  5. package/src/assets/web-panel/assets/AppLayout-Duu8xR14.js +9 -0
  6. package/src/assets/web-panel/assets/{Artifacts-L8781rE8.js → Artifacts-Br2YBbh_.js} +1 -1
  7. package/src/assets/web-panel/assets/{Audit-CffB6WIP.js → Audit-C3C6a482.js} +1 -1
  8. package/src/assets/web-panel/assets/{BackgroundAgents-BkLhayyY.js → BackgroundAgents-CzHvebcj.js} +1 -1
  9. package/src/assets/web-panel/assets/{Backup-BQnwFI78.js → Backup-DZNHYDFd.js} +1 -1
  10. package/src/assets/web-panel/assets/{BaseInput-xuUGHQch.js → BaseInput-VRiC4fIx.js} +1 -1
  11. package/src/assets/web-panel/assets/{Chat-feQXwWrg.js → Chat-Cm08WWAD.js} +5 -5
  12. package/src/assets/web-panel/assets/ChatBubbleRenderer-CGHYrugQ.js +1 -0
  13. package/src/assets/web-panel/assets/{Checkbox-DVaIiUGW.js → Checkbox-DtByk7d7.js} +1 -1
  14. package/src/assets/web-panel/assets/{Codegen-D2kEnzeU.js → Codegen-CphXXOfD.js} +1 -1
  15. package/src/assets/web-panel/assets/{Col-azDoyusS.js → Col-Hz86TnLR.js} +1 -1
  16. package/src/assets/web-panel/assets/{Community-OZdnPWM5.js → Community-HP03ltpD.js} +1 -1
  17. package/src/assets/web-panel/assets/{Compact-VDMvBN-C.js → Compact-B_N1mqQz.js} +1 -1
  18. package/src/assets/web-panel/assets/{Compliance-CPJ_zgQ1.js → Compliance-CVztRpi8.js} +1 -1
  19. package/src/assets/web-panel/assets/{Cowork-iRBwcsOA.js → Cowork-wUVOyZih.js} +4 -4
  20. package/src/assets/web-panel/assets/{Cron-DrxrspUz.js → Cron-DW5TLJKw.js} +2 -2
  21. package/src/assets/web-panel/assets/{Crosschain-D-rkAZXF.js → Crosschain-CrAFGhWH.js} +1 -1
  22. package/src/assets/web-panel/assets/{DID-5Pp1Xnmr.js → DID-pA-oD8ke.js} +2 -2
  23. package/src/assets/web-panel/assets/Dashboard-CXrd5gAw.js +3 -0
  24. package/src/assets/web-panel/assets/{Dropdown-BWj_E0Ie.js → Dropdown-EweJcsek.js} +1 -1
  25. package/src/assets/web-panel/assets/{EmailListRenderer-BT-wgSEh.js → EmailListRenderer-C9xVLPG4.js} +1 -1
  26. package/src/assets/web-panel/assets/{FamilyGuardDashboard-1PzH23pa.js → FamilyGuardDashboard-CDcEEqlu.js} +1 -1
  27. package/src/assets/web-panel/assets/{Federation-BxMIXGC6.js → Federation-DtZda9A_.js} +1 -1
  28. package/src/assets/web-panel/assets/{FormItemContext-CyJobBln.js → FormItemContext-CiYdcVz3.js} +1 -1
  29. package/src/assets/web-panel/assets/{GenericCardRenderer-YkjFuQTN.js → GenericCardRenderer-DA13uwA0.js} +1 -1
  30. package/src/assets/web-panel/assets/{Git-C2a98Wp_.js → Git-DzRhZD1O.js} +2 -2
  31. package/src/assets/web-panel/assets/{Governance-DKL3Fl84.js → Governance-D5RMgOSb.js} +1 -1
  32. package/src/assets/web-panel/assets/{Inference-BJ2cQC_3.js → Inference-BgrPfSNk.js} +1 -1
  33. package/src/assets/web-panel/assets/{KnowledgeGraph-DBAsOngt.js → KnowledgeGraph-DzjnD571.js} +1 -1
  34. package/src/assets/web-panel/assets/{Logs-COt8g9LO.js → Logs-BhWzVDtN.js} +2 -2
  35. package/src/assets/web-panel/assets/{MarkdownRenderer-3Lfht1Fd.js → MarkdownRenderer-BDape9cx.js} +1 -1
  36. package/src/assets/web-panel/assets/{Marketplace-C9Zk9Pb2.js → Marketplace-DJtL5GV7.js} +1 -1
  37. package/src/assets/web-panel/assets/{McpTools-DDWDFWPO.js → McpTools-CZcQygI7.js} +5 -5
  38. package/src/assets/web-panel/assets/{Memory-BRmVye_o.js → Memory-DUWSDUUu.js} +2 -2
  39. package/src/assets/web-panel/assets/{MobileBridge-CppBbWpD.js → MobileBridge-BTwq2WKY.js} +1 -1
  40. package/src/assets/web-panel/assets/MobileProjects-NZvvfDME.js +1 -0
  41. package/src/assets/web-panel/assets/{Mtc-CcWpeGa6.js → Mtc-BiGugEQm.js} +6 -6
  42. package/src/assets/web-panel/assets/{MtcAudit-BcllEq0K.js → MtcAudit-DY7a6gUe.js} +4 -4
  43. package/src/assets/web-panel/assets/{Multisig-BFD1CarL.js → Multisig-BQbDu_w7.js} +3 -3
  44. package/src/assets/web-panel/assets/{NLProgramming-QNXXYvjz.js → NLProgramming-CM2wXTLv.js} +1 -1
  45. package/src/assets/web-panel/assets/{Notes-07YwgQVy.js → Notes-DI6gGs1v.js} +4 -4
  46. package/src/assets/web-panel/assets/{NotificationSettings-bOccEInE.js → NotificationSettings-BsZOLWAB.js} +1 -1
  47. package/src/assets/web-panel/assets/{OrderTableRenderer-DlRi29WL.js → OrderTableRenderer-C48mu09U.js} +1 -1
  48. package/src/assets/web-panel/assets/{Organization-CBaezzEr.js → Organization-Cds0_tXr.js} +4 -4
  49. package/src/assets/web-panel/assets/{Overflow-Bl5NohFO.js → Overflow-WunYMD87.js} +1 -1
  50. package/src/assets/web-panel/assets/{P2P-CLMal9Aw.js → P2P-Bo6j4DDv.js} +2 -2
  51. package/src/assets/web-panel/assets/{PdhVaultBrowser-CGO0NITk.js → PdhVaultBrowser-Bh2XPD5f.js} +3 -3
  52. package/src/assets/web-panel/assets/{Permissions-CPSw48E2.js → Permissions-EMvMMC8N.js} +3 -3
  53. package/src/assets/web-panel/assets/{PersonalDataHub-CDM5n134.js → PersonalDataHub-AX3ypUDV.js} +2 -2
  54. package/src/assets/web-panel/assets/{Pipeline-BdsqG7WV.js → Pipeline-BOx8HOgL.js} +1 -1
  55. package/src/assets/web-panel/assets/{Privacy-B6L4E-M7.js → Privacy-tBeTVQVM.js} +1 -1
  56. package/src/assets/web-panel/assets/{ProjectInit-DXPyZKRs.js → ProjectInit-CVYg1W4M.js} +2 -2
  57. package/src/assets/web-panel/assets/{ProjectSettings-BrCC3xYT.js → ProjectSettings-Dvt5I7y1.js} +2 -2
  58. package/src/assets/web-panel/assets/{Projects-BYmuHC2q.js → Projects-Cc7FC9-e.js} +1 -1
  59. package/src/assets/web-panel/assets/{Providers-CbCe2eUl.js → Providers-Ct8xqY-E.js} +1 -1
  60. package/src/assets/web-panel/assets/{QrScannerModal-DkukgCrR.js → QrScannerModal-Cni8Ooja.js} +1 -1
  61. package/src/assets/web-panel/assets/{QuickAsk-D-htOrV8.js → QuickAsk-BJVLKdxr.js} +1 -1
  62. package/src/assets/web-panel/assets/{Recommend-Bm7SIfzu.js → Recommend-DPG8Rbbm.js} +1 -1
  63. package/src/assets/web-panel/assets/{RemoteSession-C9rP-iMc.js → RemoteSession-DsdR7dCt.js} +2 -2
  64. package/src/assets/web-panel/assets/{Reputation-kGlUgbsl.js → Reputation-Csg1YM-b.js} +1 -1
  65. package/src/assets/web-panel/assets/{Row-PBupbbUA.js → Row-D9DoFoEf.js} +1 -1
  66. package/src/assets/web-panel/assets/{RssFeed-njlwKUGK.js → RssFeed-BsvKrXMS.js} +2 -2
  67. package/src/assets/web-panel/assets/{Search-DLdEmgBw.js → Search-DFUStfgQ.js} +1 -1
  68. package/src/assets/web-panel/assets/{Security-SXGPSHdu.js → Security-ILg1zxdK.js} +4 -4
  69. package/src/assets/web-panel/assets/{Services-CuFGnNsb.js → Services-Dd41PCaY.js} +2 -2
  70. package/src/assets/web-panel/assets/{Skeleton-CtUC0mDa.js → Skeleton-Bzo1cN80.js} +1 -1
  71. package/src/assets/web-panel/assets/{Skills-P1qgOVFn.js → Skills-BDHh17P0.js} +1 -1
  72. package/src/assets/web-panel/assets/{Sla-CsppE8YV.js → Sla-jl7UTiRY.js} +1 -1
  73. package/src/assets/web-panel/assets/{SpeechSettings-Cq1oG56m.js → SpeechSettings-BePa-RF6.js} +1 -1
  74. package/src/assets/web-panel/assets/{SyncSettings-DdKW6V-C.js → SyncSettings-DiqqDKGZ.js} +2 -2
  75. package/src/assets/web-panel/assets/{Tasks-BCc9san5.js → Tasks-DYQ_6lxj.js} +1 -1
  76. package/src/assets/web-panel/assets/{Templates-CcHC1dMA.js → Templates-JiDeI06L.js} +1 -1
  77. package/src/assets/web-panel/assets/{Tenant-CA4PqU22.js → Tenant-hhgI6zwS.js} +1 -1
  78. package/src/assets/web-panel/assets/Terminal-CjTZPa37.js +3 -0
  79. package/src/assets/web-panel/assets/{TimelineRenderer-DHWTSvOy.js → TimelineRenderer-B8uB5Llk.js} +1 -1
  80. package/src/assets/web-panel/assets/{Tokens-Ddz-P_yh.js → Tokens-C3b_6r2z.js} +1 -1
  81. package/src/assets/web-panel/assets/{Trigger-ePmrmLZ1.js → Trigger-tIP7HKVe.js} +1 -1
  82. package/src/assets/web-panel/assets/{Trust-DmuK1CCE.js → Trust-vjoc11vJ.js} +1 -1
  83. package/src/assets/web-panel/assets/{UkeySign-CqqBuRrT.js → UkeySign-DUPBnGuH.js} +1 -1
  84. package/src/assets/web-panel/assets/{VideoEditing-BGlR3uWM.js → VideoEditing-CBvhUY5T.js} +1 -1
  85. package/src/assets/web-panel/assets/{Wallet-Ba_Qx6Ga.js → Wallet-DUaQ0uA5.js} +4 -4
  86. package/src/assets/web-panel/assets/{WebAuthn-29neXKt2.js → WebAuthn-DllSivjE.js} +5 -5
  87. package/src/assets/web-panel/assets/{WorkflowEditor-lD0lR0UQ.js → WorkflowEditor-xc8D3uO6.js} +1 -1
  88. package/src/assets/web-panel/assets/{chat-B3stH01u.js → chat-BVicU7oA.js} +1 -1
  89. package/src/assets/web-panel/assets/{colors-Czly56MN.js → colors-DJzLmhLm.js} +1 -1
  90. package/src/assets/web-panel/assets/{compact-item-C0TkbkAJ.js → compact-item-BvYRfThE.js} +1 -1
  91. package/src/assets/web-panel/assets/{createContext-BJmpSlf1.js → createContext-DGXxCBKR.js} +1 -1
  92. package/src/assets/web-panel/assets/devWarning-BT46Y_QI.js +1 -0
  93. package/src/assets/web-panel/assets/{hasIn-D1q-zw7u.js → hasIn-SBDJB1y4.js} +1 -1
  94. package/src/assets/web-panel/assets/{index-DV-twjNO.js → index-1SdDS6lR.js} +1 -1
  95. package/src/assets/web-panel/assets/{index-B-kTvMq7.js → index-9gdI7uUW.js} +1 -1
  96. package/src/assets/web-panel/assets/index-B7nuIywP.js +1 -0
  97. package/src/assets/web-panel/assets/{index-Bm_-RKal.js → index-BJZf9DhV.js} +1 -1
  98. package/src/assets/web-panel/assets/{index-CQftlZn-.js → index-BO1itZaY.js} +3 -3
  99. package/src/assets/web-panel/assets/{index-Dw3L_smW.js → index-BTrBGBJs.js} +1 -1
  100. package/src/assets/web-panel/assets/{index-C7sFKQ53.js → index-BYqz4Wrr.js} +1 -1
  101. package/src/assets/web-panel/assets/{index-DlvsSuBC.js → index-Bf-ctERC.js} +1 -1
  102. package/src/assets/web-panel/assets/{index-ChESIe5r.js → index-CKyEivf8.js} +1 -1
  103. package/src/assets/web-panel/assets/{index-DaqEHaCg.js → index-CdGY9P8a.js} +1 -1
  104. package/src/assets/web-panel/assets/{index-DZeoKL6t.js → index-Cf-O_g_8.js} +1 -1
  105. package/src/assets/web-panel/assets/{index-XWV8JWMc.js → index-CloCgBUR.js} +1 -1
  106. package/src/assets/web-panel/assets/{index-CpMIdPJ0.js → index-CoJAO1Lm.js} +1 -1
  107. package/src/assets/web-panel/assets/{index-CgS8XUBU.js → index-Crk5NTb0.js} +1 -1
  108. package/src/assets/web-panel/assets/{index-DmUta2lw.js → index-Cs2rmLFt.js} +1 -1
  109. package/src/assets/web-panel/assets/{index-Dpz_7sf-.js → index-CtwqO5qo.js} +1 -1
  110. package/src/assets/web-panel/assets/{index-DRcQsPez.js → index-D7HimK0M.js} +1 -1
  111. package/src/assets/web-panel/assets/{index-BEt2-3x5.js → index-DCdRAgjy.js} +1 -1
  112. package/src/assets/web-panel/assets/{index-CHvPlzWJ.js → index-DCqRS6da.js} +1 -1
  113. package/src/assets/web-panel/assets/{index-BDHXx65P.js → index-DHVLJ8q0.js} +1 -1
  114. package/src/assets/web-panel/assets/{index-K4YaPDOn.js → index-DKrvvWm4.js} +1 -1
  115. package/src/assets/web-panel/assets/{index-C1pOqU7D.js → index-DMarmfrv.js} +1 -1
  116. package/src/assets/web-panel/assets/{index-DAhrn9EJ.js → index-DRfiXB_p.js} +1 -1
  117. package/src/assets/web-panel/assets/{index-cPjWjvmW.js → index-Dm_77jgR.js} +1 -1
  118. package/src/assets/web-panel/assets/{index-C27TnOoT.js → index-DnjPdvR6.js} +1 -1
  119. package/src/assets/web-panel/assets/{index-upY9rkGZ.js → index-DonCnfMr.js} +1 -1
  120. package/src/assets/web-panel/assets/{index-CbvNJUwu.js → index-DvUTDYXX.js} +1 -1
  121. package/src/assets/web-panel/assets/{index-BVwJ3jht.js → index-FxZzRyE1.js} +1 -1
  122. package/src/assets/web-panel/assets/{index-DIF6XHIi.js → index-MjC4G09d.js} +1 -1
  123. package/src/assets/web-panel/assets/{index-B8KqsomG.js → index-VMfwIH5G.js} +1 -1
  124. package/src/assets/web-panel/assets/{index-8GKykbS4.js → index-YJkzy8ZF.js} +1 -1
  125. package/src/assets/web-panel/assets/{index-DpGs96FC.js → index-d5BVd-cL.js} +1 -1
  126. package/src/assets/web-panel/assets/{index-DO8xOwxi.js → index-e4fXH_WO.js} +1 -1
  127. package/src/assets/web-panel/assets/{index-Cu_4HaUS.js → index-hxTODbM1.js} +1 -1
  128. package/src/assets/web-panel/assets/{index-Cf7Gr3NG.js → index-lpaGoTH3.js} +1 -1
  129. package/src/assets/web-panel/assets/{index-DBqAm8es.js → index-wcCL-miV.js} +1 -1
  130. package/src/assets/web-panel/assets/{index-ZDUhX41Z.js → index-xfluRkCT.js} +1 -1
  131. package/src/assets/web-panel/assets/index-ycwAfC-i.js +1 -0
  132. package/src/assets/web-panel/assets/{index-z8ATo2aQ.js → index-z1ZylsLq.js} +1 -1
  133. package/src/assets/web-panel/assets/{initDefaultProps-QWEXi6-t.js → initDefaultProps-C1zO1KnY.js} +1 -1
  134. package/src/assets/web-panel/assets/{motion-BfrQwvV1.js → motion-CWLtEGmC.js} +1 -1
  135. package/src/assets/web-panel/assets/{move-Dn7x0H9t.js → move-DzD_C3Vm.js} +1 -1
  136. package/src/assets/web-panel/assets/{mtc-parser-F61D_TgE.js → mtc-parser-D880PYJl.js} +1 -1
  137. package/src/assets/web-panel/assets/{omit-4SQZ39PF.js → omit-CFR_z2ov.js} +1 -1
  138. package/src/assets/web-panel/assets/{pickAttrs-C7DTe126.js → pickAttrs-srIhgrSC.js} +1 -1
  139. package/src/assets/web-panel/assets/{placementArrow-40NeyzDK.js → placementArrow-C1rXpibP.js} +1 -1
  140. package/src/assets/web-panel/assets/{responsiveObserve-BiUJ6qWs.js → responsiveObserve-C30gRcSS.js} +1 -1
  141. package/src/assets/web-panel/assets/{slide-CacJTM3f.js → slide-CvtKiMfV.js} +1 -1
  142. package/src/assets/web-panel/assets/{statusUtils-K812HB-k.js → statusUtils-DNHO7ITG.js} +1 -1
  143. package/src/assets/web-panel/assets/{styleChecker-CKeHA2eM.js → styleChecker-Dt_Sl-g4.js} +1 -1
  144. package/src/assets/web-panel/assets/{useFlexGapSupport-CKl4FqXF.js → useFlexGapSupport-Cjev42Gh.js} +1 -1
  145. package/src/assets/web-panel/assets/{useFs-DPoiJjSZ.js → useFs-Baxglj2V.js} +1 -1
  146. package/src/assets/web-panel/assets/{usePersonalDataHub-aHwBspmt.js → usePersonalDataHub-B4NkcLaP.js} +1 -1
  147. package/src/assets/web-panel/assets/{vnode-DrA-GuCT.js → vnode-mIRkdT7o.js} +1 -1
  148. package/src/assets/web-panel/assets/{zoom-G3LMswgy.js → zoom-HzI5DlH_.js} +1 -1
  149. package/src/assets/web-panel/index.html +1 -1
  150. package/src/auth/npm-auth.js +30 -5
  151. package/src/commands/agenda.js +64 -2
  152. package/src/commands/agent.js +45 -3
  153. package/src/commands/background-session.js +103 -0
  154. package/src/commands/config.js +70 -5
  155. package/src/commands/context.js +36 -2
  156. package/src/commands/hub.js +19 -1
  157. package/src/commands/memory.js +21 -6
  158. package/src/commands/permissions.js +57 -1
  159. package/src/commands/plugin.js +2 -0
  160. package/src/commands/review.js +23 -11
  161. package/src/commands/routine.js +31 -8
  162. package/src/commands/session.js +11 -3
  163. package/src/commands/team.js +20 -7
  164. package/src/commands/update.js +65 -10
  165. package/src/esm-bridge.js +3 -0
  166. package/src/gateways/terminal/PtyManager.js +8 -1
  167. package/src/gateways/ws/message-dispatcher.js +3 -0
  168. package/src/gateways/ws/session-protocol.js +26 -0
  169. package/src/gateways/ws/ws-agent-handler.js +107 -69
  170. package/src/gateways/ws/ws-server.js +92 -4
  171. package/src/harness/jsonl-session-store.js +13 -6
  172. package/src/harness/mcp-client.js +200 -1
  173. package/src/index.js +123 -55
  174. package/src/lazy-dispatch.js +86 -85
  175. package/src/lib/__tests__/personal-data-hub-aichat-wizard.test.js +15 -0
  176. package/src/lib/agent-coordinator.js +4 -0
  177. package/src/lib/agent-ipc-bus.js +32 -2
  178. package/src/lib/agent-sandbox.js +16 -2
  179. package/src/lib/agent-schedule-store.js +55 -0
  180. package/src/lib/agent-tool-admission.js +104 -0
  181. package/src/lib/api-key-helper.js +10 -2
  182. package/src/lib/async-hook-supervisor.cjs +26 -4
  183. package/src/lib/background-interaction-resolver.js +71 -0
  184. package/src/lib/background-session-transport.js +59 -1
  185. package/src/lib/changelog.js +19 -11
  186. package/src/lib/channels/channel-manager.js +6 -0
  187. package/src/lib/channels/telegram-channel.js +11 -1
  188. package/src/lib/channels/webhook-channel.js +15 -3
  189. package/src/lib/checkpoint-store.js +10 -2
  190. package/src/lib/cloud/bundle.js +12 -2
  191. package/src/lib/code-review.js +32 -8
  192. package/src/lib/computer-use/control-backend.js +18 -3
  193. package/src/lib/context-breakdown.js +46 -0
  194. package/src/lib/context-source-ledger.js +43 -0
  195. package/src/lib/crash-reporter.js +6 -0
  196. package/src/lib/diff-review-follow-up.js +154 -0
  197. package/src/lib/downloader.js +17 -3
  198. package/src/lib/ensure-utf8.js +11 -3
  199. package/src/lib/event-runtime-producer.js +58 -0
  200. package/src/lib/event-runtime-store.js +354 -0
  201. package/src/lib/event-runtime-worker.js +109 -0
  202. package/src/lib/execution-backend.js +72 -21
  203. package/src/lib/git-integration.js +19 -4
  204. package/src/lib/hook-manager.js +34 -15
  205. package/src/lib/hook-runner.cjs +48 -3
  206. package/src/lib/hooks-v2-producers.js +15 -0
  207. package/src/lib/hooks-v2-runtime.js +70 -21
  208. package/src/lib/host-adb-bridge.js +111 -21
  209. package/src/lib/ide-context.js +128 -1
  210. package/src/lib/interaction-adapter.js +39 -0
  211. package/src/lib/ipc-attach-protocol.js +98 -0
  212. package/src/lib/json-schema-validate.js +190 -36
  213. package/src/lib/lan-pairing-preflight.js +13 -2
  214. package/src/lib/lsp/__tests__/lsp-server-registry.test.js +18 -0
  215. package/src/lib/lsp/lsp-client.js +18 -1
  216. package/src/lib/lsp/lsp-manager.js +4 -0
  217. package/src/lib/lsp/lsp-server-registry.js +11 -0
  218. package/src/lib/mcp-oauth.js +8 -2
  219. package/src/lib/npm-invocation.js +29 -0
  220. package/src/lib/packer/pkg-runner.js +13 -2
  221. package/src/lib/packer/precheck.js +33 -16
  222. package/src/lib/packer/web-panel-builder.js +16 -2
  223. package/src/lib/permission-rules.cjs +5 -1
  224. package/src/lib/personal-data-hub-aichat-wizard.js +22 -6
  225. package/src/lib/personal-data-hub-wiring.js +27 -3
  226. package/src/lib/plan-mode.js +204 -11
  227. package/src/lib/plugin-monitor-supervisor.js +39 -4
  228. package/src/lib/plugin-runtime/bin.js +45 -0
  229. package/src/lib/plugin-runtime/hooks.js +14 -1
  230. package/src/lib/plugin-runtime/install.js +2 -1
  231. package/src/lib/plugin-runtime/lsp.js +3 -0
  232. package/src/lib/plugin-runtime/mcp.js +8 -1
  233. package/src/lib/plugin-runtime/monitors.js +4 -0
  234. package/src/lib/plugin-runtime/plugin-options.js +74 -7
  235. package/src/lib/plugin-runtime/signature.js +45 -0
  236. package/src/lib/pr-create.js +21 -8
  237. package/src/lib/pr-link-ledger.js +15 -3
  238. package/src/lib/process-execution-broker/credential-agent.js +295 -0
  239. package/src/lib/process-execution-broker/index.js +440 -14
  240. package/src/lib/process-execution-broker/patch-child-process.js +2 -3
  241. package/src/lib/process-execution-broker/platform-sandbox.js +351 -0
  242. package/src/lib/process-manager.js +18 -3
  243. package/src/lib/provenance-api.js +6 -0
  244. package/src/lib/proxy-bootstrap.js +19 -0
  245. package/src/lib/publish-workspace.js +14 -6
  246. package/src/lib/repl-rewind.js +9 -4
  247. package/src/lib/secret-store.js +159 -0
  248. package/src/lib/service-manager.js +64 -29
  249. package/src/lib/session-hooks.js +328 -473
  250. package/src/lib/session-telemetry-middleware.js +6 -0
  251. package/src/lib/settings-hook-events.cjs +61 -1
  252. package/src/lib/settings-hooks.cjs +4 -0
  253. package/src/lib/side-effect-ledger.js +22 -0
  254. package/src/lib/sub-agent-context.js +5 -0
  255. package/src/lib/sub-agent-profiles.js +4 -0
  256. package/src/lib/trace-api.js +6 -0
  257. package/src/lib/turn-context.js +14 -8
  258. package/src/lib/unattended-action-policy.js +83 -0
  259. package/src/lib/update-notice.js +9 -45
  260. package/src/lib/with-file-lock.js +7 -0
  261. package/src/lib/ws-server.js +1 -0
  262. package/src/repl/agent-repl.js +71 -13
  263. package/src/repl/clipboard-copy.js +8 -2
  264. package/src/repl/pr-comments.js +30 -7
  265. package/src/runtime/agent-core.js +439 -87
  266. package/src/runtime/agent-runtime.js +1 -0
  267. package/src/runtime/coding-agent-contract-shared.cjs +52 -0
  268. package/src/runtime/coding-agent-policy.cjs +20 -0
  269. package/src/runtime/diagnostics.js +73 -8
  270. package/src/runtime/headless-runner.js +63 -0
  271. package/src/runtime/headless-stream.js +269 -17
  272. package/src/runtime/mcp-config.js +16 -1
  273. package/src/workers/background-agent-worker.js +80 -1
  274. package/src/assets/web-panel/assets/AppLayout-7iSKDmlI.js +0 -9
  275. package/src/assets/web-panel/assets/ChatBubbleRenderer-DcbUHeGW.js +0 -1
  276. package/src/assets/web-panel/assets/Dashboard-BAKYNavw.js +0 -3
  277. package/src/assets/web-panel/assets/MobileProjects-C4GKn1dH.js +0 -1
  278. package/src/assets/web-panel/assets/Terminal-Fj7ea0In.js +0 -3
  279. package/src/assets/web-panel/assets/devWarning-CDI8Sbaj.js +0 -1
  280. package/src/assets/web-panel/assets/index-96LBR96V.js +0 -1
  281. package/src/assets/web-panel/assets/index-DyQkiW24.js +0 -1
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Session telemetry middleware stub
3
+ */
4
+ export function registerSessionTelemetryMiddleware(program) {
5
+ // No-op stub for tests
6
+ }
@@ -21,6 +21,26 @@ const { runHooks } = require("./hook-runner.cjs");
21
21
  const { buildHookEnvelope } = require("./hook-event-bus.cjs");
22
22
  const hookEventLog = require("./hook-event-log.cjs");
23
23
 
24
+ /**
25
+ * Publish lifecycle events to Hooks v2 without making the legacy settings hook
26
+ * path depend on the ESM runtime. Payloads are deliberately reduced before
27
+ * publication: prompt text and file contents must never become durable hook
28
+ * context merely because a lifecycle producer fired.
29
+ */
30
+ function publishHooksV2Event(event, payload = {}) {
31
+ const safe = { ...payload };
32
+ delete safe.prompt;
33
+ delete safe.content;
34
+ delete safe.files;
35
+ void import("./hooks-v2-producers.js")
36
+ .then(({ emitHooksV2Event }) => {
37
+ emitHooksV2Event(event, safe);
38
+ })
39
+ .catch(() => {
40
+ // Optional producer bridge; legacy settings hooks remain authoritative.
41
+ });
42
+ }
43
+
24
44
  /**
25
45
  * Stamp a unified-bus delivery id (event_id) onto a hook payload (P2 event bus).
26
46
  * Additive — a hook that ignores it is unaffected; a hook that reads it gets a
@@ -104,6 +124,10 @@ function runUserPromptSubmitHooks(
104
124
  settingsHooks,
105
125
  { prompt, cwd, sessionId } = {},
106
126
  ) {
127
+ publishHooksV2Event("UserPromptSubmit", {
128
+ cwd: cwd || null,
129
+ session_id: sessionId || null,
130
+ });
107
131
  if (!settingsHooks) return { blocked: false, additionalContext: null };
108
132
  const matched = collectHooks(settingsHooks, "UserPromptSubmit", "");
109
133
  if (matched.length === 0) return { blocked: false, additionalContext: null };
@@ -159,7 +183,7 @@ function dispatchAsyncHooks(settingsHooks, event, payload = {}, opts = {}) {
159
183
  return supervisor.dispatch(
160
184
  asyncHooks,
161
185
  { hook_event_name: event, cwd: opts.cwd, ...payload },
162
- { cwd: opts.cwd },
186
+ { cwd: opts.cwd, broker: opts.broker },
163
187
  );
164
188
  }
165
189
 
@@ -169,6 +193,11 @@ function dispatchAsyncHooks(settingsHooks, event, payload = {}, opts = {}) {
169
193
  * @returns {{ additionalContext:string|null }}
170
194
  */
171
195
  function runSessionStartHooks(settingsHooks, { source, cwd, sessionId } = {}) {
196
+ publishHooksV2Event("SessionStart", {
197
+ source: source || "startup",
198
+ cwd: cwd || null,
199
+ session_id: sessionId || null,
200
+ });
172
201
  if (!settingsHooks) return { additionalContext: null };
173
202
  const matched = collectHooks(settingsHooks, "SessionStart", source || "");
174
203
  if (matched.length === 0) return { additionalContext: null };
@@ -199,6 +228,11 @@ function runCwdChangedHooks(
199
228
  settingsHooks,
200
229
  { oldCwd, newCwd, cwd, sessionId } = {},
201
230
  ) {
231
+ publishHooksV2Event("CwdChanged", {
232
+ old_cwd: oldCwd || null,
233
+ cwd: newCwd || cwd || null,
234
+ session_id: sessionId || null,
235
+ });
202
236
  if (!settingsHooks) return { additionalContext: null };
203
237
  const matched = collectHooks(settingsHooks, "CwdChanged", newCwd || "");
204
238
  const { sync } = partitionAsyncHooks(matched);
@@ -234,6 +268,13 @@ function runWorktreeCreateHooks(
234
268
  settingsHooks,
235
269
  { worktreePath, branch, baseSha, cwd, sessionId } = {},
236
270
  ) {
271
+ publishHooksV2Event("WorktreeCreate", {
272
+ worktree_path: worktreePath || null,
273
+ branch: branch || null,
274
+ base_sha: baseSha || null,
275
+ cwd: cwd || worktreePath || null,
276
+ session_id: sessionId || null,
277
+ });
237
278
  if (!settingsHooks) return { additionalContext: null };
238
279
  const matched = collectHooks(settingsHooks, "WorktreeCreate", branch || "");
239
280
  const { sync } = partitionAsyncHooks(matched);
@@ -271,6 +312,14 @@ function runWorktreeRemoveHooks(
271
312
  settingsHooks,
272
313
  { worktreePath, branch, removed, reason, cwd, sessionId } = {},
273
314
  ) {
315
+ publishHooksV2Event("WorktreeRemove", {
316
+ worktree_path: worktreePath || null,
317
+ branch: branch || null,
318
+ removed: removed === true,
319
+ reason: reason || null,
320
+ cwd: cwd || worktreePath || null,
321
+ session_id: sessionId || null,
322
+ });
274
323
  if (!settingsHooks) return { additionalContext: null };
275
324
  const matched = collectHooks(settingsHooks, "WorktreeRemove", branch || "");
276
325
  const { sync } = partitionAsyncHooks(matched);
@@ -312,6 +361,11 @@ function runInstructionsLoadedHooks(
312
361
  settingsHooks,
313
362
  { files, cwd, sessionId } = {},
314
363
  ) {
364
+ publishHooksV2Event("InstructionsLoaded", {
365
+ count: Array.isArray(files) ? files.length : 0,
366
+ cwd: cwd || null,
367
+ session_id: sessionId || null,
368
+ });
315
369
  if (!settingsHooks) return { additionalContext: null };
316
370
  const matched = collectHooks(settingsHooks, "InstructionsLoaded", "");
317
371
  const { sync } = partitionAsyncHooks(matched);
@@ -353,6 +407,12 @@ function runObserveHooks(
353
407
  payload = {},
354
408
  { cwd, matchTarget, traceId, parentId } = {},
355
409
  ) {
410
+ publishHooksV2Event(event, {
411
+ ...payload,
412
+ cwd: cwd || payload.cwd || null,
413
+ trace_id: traceId || payload.trace_id || null,
414
+ parent_id: parentId || payload.parent_id || null,
415
+ });
356
416
  if (!settingsHooks) return { decision: "continue", results: [] };
357
417
  const matched = collectHooks(settingsHooks, event, matchTarget || "");
358
418
  const { sync } = partitionAsyncHooks(matched);
@@ -252,6 +252,10 @@ function collectHooks(hooksBlock, event, toolName) {
252
252
  event,
253
253
  async: h.async === true,
254
254
  asyncRewake: h.asyncRewake === true,
255
+ origin: h.origin || "hook",
256
+ pluginId: h.pluginId || null,
257
+ pluginVersion: h.pluginVersion || null,
258
+ pluginSource: h.pluginSource || null,
255
259
  });
256
260
  }
257
261
  }
@@ -56,6 +56,8 @@ const KIND_IDEMPOTENT = new Map([
56
56
  ["network-mutation", false],
57
57
  ["payment", false],
58
58
  ["file-write", false],
59
+ ["file-delete", false],
60
+ ["file-move", false],
59
61
  ["file-write-checkpointed", true],
60
62
  ["read", true],
61
63
  ["compute", true],
@@ -99,6 +101,18 @@ export function classifyToolSideEffect(toolName, args = {}) {
99
101
  kind: "file-write",
100
102
  key: shortKey(a.path || a.file || a.notebook_path),
101
103
  };
104
+ case "delete_file":
105
+ return {
106
+ kind: "file-delete",
107
+ key: shortKey(a.path || a.file),
108
+ };
109
+ case "move_file":
110
+ return {
111
+ kind: "file-move",
112
+ key: shortKey(
113
+ `${a.path || a.file || ""} -> ${a.target_path || a.targetPath || ""}`,
114
+ ),
115
+ };
102
116
  case "edit_file_hashed":
103
117
  // Hash-guarded: re-applying over an already-changed tree fails safely, so
104
118
  // a replay cannot silently double-apply — idempotent-by-construction.
@@ -201,6 +215,14 @@ export class SideEffectLedger {
201
215
  ]);
202
216
  }
203
217
 
218
+ /** Merge bounded correlation metadata discovered after an effect started. */
219
+ annotate(opId, metadata = {}) {
220
+ const rec = this._rec(opId);
221
+ if (!rec || !metadata || typeof metadata !== "object") return this;
222
+ rec.meta = { ...rec.meta, ...metadata };
223
+ return this;
224
+ }
225
+
204
226
  /** Confirm the effect applied. Only from `started`. */
205
227
  commit(opId) {
206
228
  return this._advance(
@@ -83,6 +83,10 @@ export class SubAgentContext {
83
83
  // nested spawns (threaded into the loop options so a nested spawn_sub_agent
84
84
  // reads it as ctx.subAgentContract).
85
85
  this.subAgentContract = options.subAgentContract || null;
86
+ // Optional session-level Extension Tier admission policy. Keep it as an
87
+ // explicit inherited capability so child loops cannot silently bypass the
88
+ // parent's admission decision.
89
+ this.toolAdmission = options.toolAdmission || null;
86
90
  // Skill capability INTERSECT: null = unrestricted; a list (possibly empty)
87
91
  // restricts run_skill/list_skills in this context's loop to those skills.
88
92
  this.skillAllowlist =
@@ -335,6 +339,7 @@ export class SubAgentContext {
335
339
  subAgentBudget: this.subAgentBudget,
336
340
  // This context's effective contract = the ceiling for its nested spawns.
337
341
  subAgentContract: this.subAgentContract,
342
+ toolAdmission: this.toolAdmission,
338
343
  // Skill allow-list restricting run_skill/list_skills in this loop.
339
344
  skillAllowlist: this.skillAllowlist,
340
345
  // Parent trace for hook envelopes (absent → key omitted, no parent_id).
@@ -30,6 +30,8 @@ const FULL_TOOLS = Object.freeze([
30
30
  "write_file",
31
31
  "edit_file",
32
32
  "edit_file_hashed",
33
+ "delete_file",
34
+ "move_file",
33
35
  "list_dir",
34
36
  "search_files",
35
37
  "search_sessions",
@@ -49,6 +51,8 @@ const DESIGN_TOOLS = Object.freeze([
49
51
  "write_file",
50
52
  "edit_file",
51
53
  "edit_file_hashed",
54
+ "delete_file",
55
+ "move_file",
52
56
  "list_dir",
53
57
  "search_files",
54
58
  "web_fetch",
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Trace API stub
3
+ */
4
+ export function recordTrace(name, data) {
5
+ // No-op stub for tests
6
+ }
@@ -11,21 +11,23 @@
11
11
  * @module turn-context
12
12
  */
13
13
 
14
- import { execSync } from "child_process";
15
14
  import path from "path";
15
+ import { executionBroker } from "./process-execution-broker/index.js";
16
16
 
17
- const _deps = { execSync };
17
+ const _deps = {
18
+ execFileSync: (...args) => executionBroker.execFileSync(...args),
19
+ };
18
20
 
19
21
  /**
20
22
  * Run a git command with stdio pipe and return stdout or null.
21
- * @param {string} cmd
23
+ * @param {string[]} args
22
24
  * @param {string} cwd
23
25
  * @returns {string|null}
24
26
  */
25
- function _git(cmd, cwd) {
27
+ function _git(args, cwd) {
26
28
  try {
27
29
  return _deps
28
- .execSync(`git ${cmd}`, {
30
+ .execFileSync("git", args, {
29
31
  cwd,
30
32
  encoding: "utf-8",
31
33
  stdio: ["ignore", "pipe", "ignore"],
@@ -33,6 +35,10 @@ function _git(cmd, cwd) {
33
35
  // `status --porcelain` on a dirty repo with many files can exceed the
34
36
  // 1 MB execSync default → ENOBUFS → null → "(clean)" misreport below.
35
37
  maxBuffer: 16 * 1024 * 1024,
38
+ origin: "context:git",
39
+ policy: "allow",
40
+ scope: "context",
41
+ shell: false,
36
42
  })
37
43
  .trim();
38
44
  } catch (_e) {
@@ -60,10 +66,10 @@ export function buildTurnContext({
60
66
  lines.push(`## Turn context (iteration ${iteration})`);
61
67
  lines.push(`- cwd: ${path.resolve(cwd)}`);
62
68
 
63
- const branch = _git("rev-parse --abbrev-ref HEAD", cwd);
69
+ const branch = _git(["rev-parse", "--abbrev-ref", "HEAD"], cwd);
64
70
  if (branch) {
65
- const head = _git("rev-parse --short HEAD", cwd);
66
- const status = _git("status --porcelain", cwd);
71
+ const head = _git(["rev-parse", "--short", "HEAD"], cwd);
72
+ const status = _git(["status", "--porcelain"], cwd);
67
73
  // Distinguish "command failed" (null — timeout / huge output) from "" (truly
68
74
  // clean): the old `status && …` reported a repo as "(clean)" whenever status
69
75
  // couldn't be determined, which is a wrong signal to the model.
@@ -121,6 +121,89 @@ export function classifyActionRisk(actionClass) {
121
121
  return "conditional"; // push — depends on branch protection
122
122
  }
123
123
 
124
+ /**
125
+ * Classify a shell command before it reaches the process broker. This is a
126
+ * deliberately conservative, token-free classifier: compound commands use
127
+ * the highest-risk segment and unknown commands remain unknown so an
128
+ * unattended caller can fail closed.
129
+ */
130
+ export function classifyShellAction(command) {
131
+ if (typeof command !== "string" || !command.trim()) return null;
132
+ const segments = command
133
+ .split(/&&|\|\||[;\n]|\|/)
134
+ .map((part) =>
135
+ part
136
+ .trim()
137
+ .replace(/^(?:env\s+)?(?:sudo\s+)?(?:command\s+)?/i, "")
138
+ .toLowerCase(),
139
+ )
140
+ .filter(Boolean);
141
+ const classifySegment = (segment) => {
142
+ if (/^git\s+push\b/.test(segment)) return ACTION_CLASS.PUSH;
143
+ if (/^git\s+(?:merge|rebase|cherry-pick)\b/.test(segment)) {
144
+ return ACTION_CLASS.MERGE;
145
+ }
146
+ if (/^(?:npm|pnpm|yarn|bun)\s+publish\b/.test(segment)) {
147
+ return ACTION_CLASS.PUBLISH;
148
+ }
149
+ if (
150
+ /^(?:terraform\s+(?:apply|destroy)|pulumi\s+up|kubectl\s+(?:apply|delete|rollout)|helm\s+(?:install|upgrade)|docker\s+push)\b/.test(
151
+ segment,
152
+ )
153
+ ) {
154
+ return segment.startsWith("docker push")
155
+ ? ACTION_CLASS.DEPLOY
156
+ : ACTION_CLASS.INFRA_MUTATION;
157
+ }
158
+ if (/\b(?:deploy|rollout)\b/.test(segment)) return ACTION_CLASS.DEPLOY;
159
+ if (/^(?:git\s+(?:status|diff|log|show|branch)|ls\b|dir\b|pwd\b|cat\b|type\b|rg\b|grep\b|find\b|where\b)/.test(segment)) {
160
+ return ACTION_CLASS.READ;
161
+ }
162
+ if (/^(?:npm|pnpm|yarn|bun)\s+(?:test|run\s+(?:test|lint|build)|install\b)/.test(segment) || /^(?:pytest|vitest|jest|cargo\s+test|go\s+test)\b/.test(segment)) {
163
+ return ACTION_CLASS.LOCAL_WRITE;
164
+ }
165
+ if (/^echo\b/.test(segment) && />/.test(segment)) {
166
+ return ACTION_CLASS.LOCAL_WRITE;
167
+ }
168
+ return null;
169
+ };
170
+ const rank = [
171
+ ACTION_CLASS.READ,
172
+ ACTION_CLASS.LOCAL_WRITE,
173
+ ACTION_CLASS.COMMIT,
174
+ ACTION_CLASS.PUSH,
175
+ ACTION_CLASS.PUBLISH,
176
+ ACTION_CLASS.MERGE,
177
+ ACTION_CLASS.DEPLOY,
178
+ ACTION_CLASS.INFRA_MUTATION,
179
+ ACTION_CLASS.EXTERNAL_MESSAGE,
180
+ ];
181
+ const classified = segments.map(classifySegment);
182
+ return classified.reduce((highest, current) => {
183
+ if (!current) return null;
184
+ if (!highest) return current;
185
+ return rank.indexOf(current) > rank.indexOf(highest) ? current : highest;
186
+ }, null);
187
+ }
188
+
189
+ /** Apply the unattended policy to a shell command, including protected push targets. */
190
+ export function evaluateUnattendedShellAction(command, params = {}) {
191
+ const actionClass = classifyShellAction(command);
192
+ const protectedBranch =
193
+ params.protectedBranch === true ||
194
+ /\bgit\s+push\b[^\n]*(?:^|\s)(?:main|master|develop|production)(?:\s|$)/i.test(
195
+ command || "",
196
+ );
197
+ return {
198
+ actionClass,
199
+ ...evaluateUnattendedAction({
200
+ ...params,
201
+ actionClass,
202
+ protectedBranch,
203
+ }),
204
+ };
205
+ }
206
+
124
207
  /**
125
208
  * Decide whether a fired task may perform one action. Fail-closed.
126
209
  *
@@ -1,39 +1,25 @@
1
1
  /**
2
- * Startup update notice — Claude-Code-style "new version available" line.
3
- *
4
- * Zero startup cost by design:
5
- * - the CLI entry only does ONE sync cache read (~/.chainlesschain/
6
- * update-check.json) and prints a single gray stderr line when the cached
7
- * latest version is newer than the running one (TTY only, never pollutes
8
- * piped/JSON output);
9
- * - when the cache is stale (>24h) it spawns a DETACHED, unref'd child that
10
- * refreshes the cache from the npm registry for the NEXT run — the current
11
- * invocation never waits on the network. The cache's checkedAt is touched
12
- * optimistically before spawning so concurrent invocations don't stampede.
13
- *
14
- * Disable with CC_UPDATE_NOTICE=0. `cc update` remains the full interactive
15
- * checker (GitHub releases + assets); this is just the passive nudge.
2
+ * Passive startup update notice. Cache reads are synchronous and fail-open;
3
+ * refreshing the npm metadata is delegated to a detached child process.
16
4
  */
17
-
18
5
  import fsDefault from "fs";
19
6
  import pathDefault from "path";
20
7
  import osDefault from "os";
21
- import { spawn as spawnDefault } from "child_process";
22
8
  import { fileURLToPath } from "url";
23
9
  import chalk from "chalk";
24
10
  import semver from "semver";
25
11
  import { VERSION } from "../constants.js";
26
12
  import { readDiskVersion } from "./version-skew.js";
13
+ import { executionBroker } from "./process-execution-broker/index.js";
27
14
 
28
15
  export const CACHE_TTL_MS = 24 * 60 * 60 * 1000;
29
16
  export const NPM_LATEST_URL =
30
17
  "https://registry.npmjs.org/chainlesschain/latest";
31
-
32
18
  export const _deps = {
33
19
  fs: fsDefault,
34
20
  path: pathDefault,
35
21
  os: osDefault,
36
- spawn: spawnDefault,
22
+ spawn: (...args) => executionBroker.spawn(...args),
37
23
  };
38
24
 
39
25
  export function cachePath(deps = _deps) {
@@ -63,34 +49,19 @@ function writeCache(deps, cache) {
63
49
  }
64
50
  }
65
51
 
66
- /**
67
- * Entry-point hook. Cheap and fail-open — never throws, never blocks.
68
- *
69
- * @returns {{ printed: boolean, spawned: boolean }}
70
- */
71
52
  export function maybeNotifyUpdate(opts = {}) {
72
53
  const deps = { ..._deps, ...(opts.deps || {}) };
73
54
  const env = opts.env || process.env;
74
55
  const now = opts.now ?? Date.now();
75
56
  const isTTY = opts.isTTY ?? Boolean(process.stderr.isTTY);
76
- // `running` = the version THIS process loaded at spawn (baked VERSION).
77
- // `installed` = the version on disk RIGHT NOW (a fresh package.json read).
78
- // They diverge after `npm i -g` runs under a long-lived process: the files
79
- // update but the running process keeps the old code. We therefore compare the
80
- // INSTALLED version against npm latest (so a stale process never nags
81
- // "npm i -g" when disk is already current), and separately flag a
82
- // running<installed skew as "restart to apply" (re-installing wouldn't help).
83
57
  const running = opts.currentVersion || VERSION;
84
58
  const installed = opts.installedVersion ?? readDiskVersion() ?? running;
85
59
  const print =
86
60
  opts.print || ((line) => process.stderr.write(chalk.gray(line) + "\n"));
87
-
88
61
  const out = { printed: false, spawned: false };
89
62
  try {
90
63
  if (env.CC_UPDATE_NOTICE === "0") return out;
91
-
92
64
  const cache = readCache(deps);
93
-
94
65
  if (
95
66
  isTTY &&
96
67
  cache?.latest &&
@@ -98,7 +69,6 @@ export function maybeNotifyUpdate(opts = {}) {
98
69
  semver.valid(installed) &&
99
70
  semver.gt(cache.latest, installed)
100
71
  ) {
101
- // A newer release exists on npm → a real upgrade is available.
102
72
  print(
103
73
  `Update available: chainlesschain ${installed} → ${cache.latest} (npm i -g chainlesschain · CC_UPDATE_NOTICE=0 to hide)`,
104
74
  );
@@ -109,19 +79,13 @@ export function maybeNotifyUpdate(opts = {}) {
109
79
  semver.valid(running) &&
110
80
  semver.gt(installed, running)
111
81
  ) {
112
- // Already updated on disk, but this process is older → restart to apply.
113
- // Re-running `npm i -g` would be a no-op; the fix is to relaunch cc / the
114
- // IDE panel so the new code loads.
115
82
  print(
116
83
  `cc was updated ${running} → ${installed} on disk — restart cc (or reload your IDE panel) to apply (CC_UPDATE_NOTICE=0 to hide)`,
117
84
  );
118
85
  out.printed = true;
119
86
  }
120
-
121
87
  const stale = !cache?.checkedAt || now - cache.checkedAt > CACHE_TTL_MS;
122
88
  if (stale) {
123
- // Optimistic touch first: parallel `cc` invocations inside the stale
124
- // window won't each spawn a refresher.
125
89
  writeCache(deps, { ...(cache || {}), checkedAt: now });
126
90
  const refresher = deps.path.join(
127
91
  deps.path.dirname(fileURLToPath(import.meta.url)),
@@ -131,20 +95,20 @@ export function maybeNotifyUpdate(opts = {}) {
131
95
  detached: true,
132
96
  stdio: "ignore",
133
97
  windowsHide: true,
98
+ origin: "update:notice-refresh",
99
+ policy: "allow",
100
+ scope: "update",
101
+ shell: false,
134
102
  });
135
103
  if (child && typeof child.unref === "function") child.unref();
136
104
  out.spawned = true;
137
105
  }
138
106
  } catch {
139
- /* fail-open: a broken cache or spawn must never affect the CLI */
107
+ /* update hints must never affect the CLI */
140
108
  }
141
109
  return out;
142
110
  }
143
111
 
144
- /**
145
- * One cache refresh (used by the detached child; exported for tests).
146
- * npm registry only — light, unauthenticated, no GitHub rate-limit risk.
147
- */
148
112
  export async function refreshCacheOnce({
149
113
  cacheFile,
150
114
  fetchImpl = fetch,
@@ -26,6 +26,7 @@ export function withFileLock(targetPath, fn, opts = {}) {
26
26
  _fs = defaultFs,
27
27
  _now = () => Date.now(),
28
28
  _sleep = sleepSync,
29
+ failIfUnavailable = false,
29
30
  } = opts;
30
31
 
31
32
  const lockDir = `${targetPath}.lock`;
@@ -54,6 +55,12 @@ export function withFileLock(targetPath, fn, opts = {}) {
54
55
  }
55
56
  }
56
57
 
58
+ if (!held && failIfUnavailable) {
59
+ const error = new Error(`Could not acquire state lock: ${targetPath}`);
60
+ error.code = "STATE_LOCK_UNAVAILABLE";
61
+ throw error;
62
+ }
63
+
57
64
  try {
58
65
  return fn({ locked: held });
59
66
  } finally {
@@ -14,4 +14,5 @@ export {
14
14
  ChainlessChainWSServer,
15
15
  tokenizeCommand,
16
16
  isCommandBlocked,
17
+ _deps,
17
18
  } from "../gateways/ws/ws-server.js";
@@ -77,6 +77,7 @@ import {
77
77
  fireUserPromptSubmit,
78
78
  fireAssistantResponse,
79
79
  fireSessionStop,
80
+ fireNotification,
80
81
  } from "../lib/session-hooks.js";
81
82
  import { HookEvents } from "../lib/hook-manager.js";
82
83
  import { IterationBudget } from "../lib/iteration-budget.js";
@@ -182,21 +183,46 @@ function persistRecentDenial(record, options = {}) {
182
183
  * user's attention (e.g. waiting on a permission/risk confirmation). A hook can
183
184
  * ring a bell / send a desktop notification. Best-effort, never blocks.
184
185
  */
185
- async function _fireNotification(message) {
186
- if (!_settingsHooks) return;
187
- try {
188
- const { runObserveHooks } = await import("../lib/settings-hook-events.cjs");
189
- runObserveHooks(
190
- _settingsHooks,
191
- "Notification",
192
- { message, cwd: process.cwd(), session_id: null },
193
- { cwd: process.cwd() },
194
- );
195
- } catch (_err) {
196
- // observe-only — never affect the prompt
186
+ async function _fireNotification(message, type = "info") {
187
+ // Fire settings.json observe hooks first (best-effort)
188
+ if (_settingsHooks) {
189
+ try {
190
+ const { runObserveHooks } =
191
+ await import("../lib/settings-hook-events.cjs");
192
+ runObserveHooks(
193
+ _settingsHooks,
194
+ "Notification",
195
+ {
196
+ message,
197
+ type,
198
+ cwd: process.cwd(),
199
+ session_id: _session?.sessionId || null,
200
+ },
201
+ { cwd: process.cwd() },
202
+ );
203
+ } catch (_err) {
204
+ // observe-only — never affect the prompt
205
+ }
197
206
  }
207
+ // Fire database-registered Notification hooks (interceptable/suppressible)
208
+ if (_session) {
209
+ try {
210
+ const result = await fireNotification(_hookDb, message, {
211
+ session: _session,
212
+ type,
213
+ });
214
+ if (result?.directive?.suppress) {
215
+ return { suppress: true };
216
+ }
217
+ if (result?.directive?.message) {
218
+ return { message: result.directive.message };
219
+ }
220
+ } catch (_err) {
221
+ // ignore hook errors
222
+ }
223
+ }
224
+ return { message };
198
225
  }
199
-
200
226
  /**
201
227
  * "Always allow" persistence: derive a sensible allow rule for a tool call,
202
228
  * append it to .claude/settings.local.json (personal, gitignored), and reflect
@@ -1736,6 +1762,38 @@ export async function startAgentRepl(options = {}) {
1736
1762
  // "close" cleanup instead of a bare exit.
1737
1763
  _replRl = rl;
1738
1764
 
1765
+ // MCP elicitation in the interactive REPL uses the same readline surface as
1766
+ // approvals, while MCPClient retains the protocol-level response shape.
1767
+ const mcpElicitationClients = [
1768
+ _adhocMcp?.mcpClient,
1769
+ _bundleMcpClient,
1770
+ ].filter((client) => client?.setElicitationHandler);
1771
+ for (const client of mcpElicitationClients) {
1772
+ client.setElicitationHandler(
1773
+ (request) =>
1774
+ new Promise((resolve) => {
1775
+ const label = request.message || "MCP server requests additional input";
1776
+ rl.question(chalk.yellow(`\n MCP [${request.server}] ${label}\n > `), (answer) => {
1777
+ const trimmed = String(answer || "").trim();
1778
+ if (!trimmed) {
1779
+ resolve({ action: "cancel" });
1780
+ } else {
1781
+ let content = { value: trimmed };
1782
+ try {
1783
+ const parsed = JSON.parse(trimmed);
1784
+ if (parsed && typeof parsed === "object") content = parsed;
1785
+ } catch {
1786
+ // Plain text remains a valid single-field elicitation answer.
1787
+ }
1788
+ resolve({ action: "accept", content });
1789
+ }
1790
+ rl.prompt();
1791
+ });
1792
+ }),
1793
+ { timeoutMs: options.mcpElicitationTimeoutMs },
1794
+ );
1795
+ }
1796
+
1739
1797
  // Inbound channels (--channels, gap-2026-07-11 P0#5): external events enter
1740
1798
  // this session as user turns. rl.emit("line") reuses the REPL's own input
1741
1799
  // path, so an event landing mid-turn queues behind it (pending-lines), and
@@ -4,7 +4,7 @@
4
4
  * spawn wrapper. The pure parts are unit-tested; real clipboard I/O is
5
5
  * environment-dependent and exercised through an injected spawnSync.
6
6
  */
7
- import { spawnSync as _spawnSync } from "node:child_process";
7
+ import { executionBroker } from "../lib/process-execution-broker/index.js";
8
8
 
9
9
  /** Flatten a message's content (string | content-parts[]) to plain text. Pure. */
10
10
  export function messageText(content) {
@@ -78,7 +78,9 @@ export function clipboardCommands(platform = process.platform) {
78
78
  ];
79
79
  }
80
80
 
81
- const _deps = { spawnSync: _spawnSync };
81
+ const _deps = {
82
+ spawnSync: (...args) => executionBroker.spawnSync(...args),
83
+ };
82
84
 
83
85
  /**
84
86
  * Write text to the system clipboard, trying each platform candidate in order
@@ -96,6 +98,10 @@ export function copyToClipboard(text, { platform, spawnSync } = {}) {
96
98
  input: text,
97
99
  encoding: "utf-8",
98
100
  windowsHide: true,
101
+ origin: "repl:clipboard-copy",
102
+ policy: "allow",
103
+ scope: "clipboard",
104
+ shell: false,
99
105
  });
100
106
  } catch (e) {
101
107
  lastErr = e.message;