chainlesschain 0.162.183 → 0.162.185

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 (205) hide show
  1. package/package.json +1 -1
  2. package/src/assets/web-panel/.build-hash +1 -1
  3. package/src/assets/web-panel/assets/{AIOps-BapS7lz4.js → AIOps-Co4ipAKx.js} +1 -1
  4. package/src/assets/web-panel/assets/{ActionButton-CXYdXEUI.js → ActionButton-BpsoDrDq.js} +1 -1
  5. package/src/assets/web-panel/assets/{Analytics-CKR5Lexn.js → Analytics-DMUqeBik.js} +3 -3
  6. package/src/assets/web-panel/assets/{AppLayout-DATVUOXB.js → AppLayout-C6fD_dXr.js} +5 -5
  7. package/src/assets/web-panel/assets/{Artifacts-vb_0ZlV1.js → Artifacts-DW0YlQlV.js} +1 -1
  8. package/src/assets/web-panel/assets/{Audit-BI1l9GWt.js → Audit-DXtHiiFK.js} +1 -1
  9. package/src/assets/web-panel/assets/{BackgroundAgents-BFXbaUEf.js → BackgroundAgents-BgmF62U-.js} +1 -1
  10. package/src/assets/web-panel/assets/{Backup-DEYBJJ6H.js → Backup-D8vhVLQC.js} +1 -1
  11. package/src/assets/web-panel/assets/{BaseInput-COdzeO0M.js → BaseInput-D7nCtY2X.js} +1 -1
  12. package/src/assets/web-panel/assets/{Chat-oSN6sfM5.js → Chat-CBZLEV2G.js} +6 -6
  13. package/src/assets/web-panel/assets/{ChatBubbleRenderer-CI_jT0ru.js → ChatBubbleRenderer-CtDb-2Qu.js} +1 -1
  14. package/src/assets/web-panel/assets/{Checkbox-BezEa4tm.js → Checkbox-DJLkeD4M.js} +1 -1
  15. package/src/assets/web-panel/assets/{Codegen-CNEL0Ife.js → Codegen-Cs6xO8Fd.js} +1 -1
  16. package/src/assets/web-panel/assets/{Col-Bf2Y2Bn1.js → Col-Dtx9wxfT.js} +1 -1
  17. package/src/assets/web-panel/assets/{Community-BfUlu4Hc.js → Community-DiigJg4n.js} +1 -1
  18. package/src/assets/web-panel/assets/{Compact-BN4xWP9e.js → Compact-B67oMDDL.js} +1 -1
  19. package/src/assets/web-panel/assets/{Compliance-Bt6BKmEP.js → Compliance-BBDl-Jos.js} +1 -1
  20. package/src/assets/web-panel/assets/{Cowork-DKLp8YxO.js → Cowork-Ba98hSsa.js} +3 -3
  21. package/src/assets/web-panel/assets/{Cron-o7Hhw6ky.js → Cron-DBWQZIz7.js} +2 -2
  22. package/src/assets/web-panel/assets/{Crosschain-DaNXWJIw.js → Crosschain-OcrKzxAt.js} +1 -1
  23. package/src/assets/web-panel/assets/{DID-B-Qa2mha.js → DID-mW2eSbjM.js} +2 -2
  24. package/src/assets/web-panel/assets/{Dashboard-CMX0Qnxw.js → Dashboard-BmsDPmKJ.js} +2 -2
  25. package/src/assets/web-panel/assets/{Dropdown-BLuLyRu_.js → Dropdown-CVzmCyRx.js} +1 -1
  26. package/src/assets/web-panel/assets/{EmailListRenderer-CFBAeMij.js → EmailListRenderer-vVgqKzhh.js} +1 -1
  27. package/src/assets/web-panel/assets/{FamilyGuardDashboard-DWzXQ67V.js → FamilyGuardDashboard-CWY5t_ZJ.js} +1 -1
  28. package/src/assets/web-panel/assets/{Federation-DB1QSLgr.js → Federation-ZGomhjxr.js} +1 -1
  29. package/src/assets/web-panel/assets/{FormItemContext-C-OKCToP.js → FormItemContext-DyaknbLr.js} +1 -1
  30. package/src/assets/web-panel/assets/{GenericCardRenderer-DMJFtFZf.js → GenericCardRenderer-CmIvN1qr.js} +1 -1
  31. package/src/assets/web-panel/assets/{Git-DsGSujOV.js → Git-DQOJIYkG.js} +2 -2
  32. package/src/assets/web-panel/assets/{Governance-B0iS9NEW.js → Governance-CMiY-EEp.js} +1 -1
  33. package/src/assets/web-panel/assets/{Inference-CH004H7c.js → Inference-D6IXuucf.js} +1 -1
  34. package/src/assets/web-panel/assets/{KnowledgeGraph-Q9MTQ83c.js → KnowledgeGraph-6jkTL0hV.js} +1 -1
  35. package/src/assets/web-panel/assets/{Logs-B_fyjZK9.js → Logs-N0jD-349.js} +2 -2
  36. package/src/assets/web-panel/assets/MarkdownRenderer-BL1IVXm-.js +1 -0
  37. package/src/assets/web-panel/assets/{Marketplace-BIStF9d1.js → Marketplace-WhYuhBxm.js} +1 -1
  38. package/src/assets/web-panel/assets/{McpTools-pGfal768.js → McpTools-DTeO28xy.js} +5 -5
  39. package/src/assets/web-panel/assets/{Memory-xbNqpIqB.js → Memory-MSHzPSE-.js} +2 -2
  40. package/src/assets/web-panel/assets/{MobileBridge-B5EQGrIK.js → MobileBridge-Cyqs3J3W.js} +1 -1
  41. package/src/assets/web-panel/assets/MobileProjects-7IN2hWHg.js +1 -0
  42. package/src/assets/web-panel/assets/{Mtc-a_rmfxJH.js → Mtc-MWBnezNM.js} +5 -5
  43. package/src/assets/web-panel/assets/{MtcAudit-BmKuofUr.js → MtcAudit-DQu35Nu-.js} +4 -4
  44. package/src/assets/web-panel/assets/{Multisig-BQbJ-CAN.js → Multisig-CQS_M-DP.js} +3 -3
  45. package/src/assets/web-panel/assets/{NLProgramming-JtPJKX25.js → NLProgramming-WD16vQvY.js} +1 -1
  46. package/src/assets/web-panel/assets/{Notes-ZFKznTSG.js → Notes-DqsiSwOq.js} +3 -3
  47. package/src/assets/web-panel/assets/{NotificationSettings-B6aHRBBR.js → NotificationSettings-D3LoxO-P.js} +1 -1
  48. package/src/assets/web-panel/assets/{OrderTableRenderer-DdcsOqEz.js → OrderTableRenderer-C5jPoTd6.js} +1 -1
  49. package/src/assets/web-panel/assets/{Organization-BFDTAUgS.js → Organization-48aMBJso.js} +4 -4
  50. package/src/assets/web-panel/assets/{Overflow-0FoTfbWH.js → Overflow-j-a2XkhR.js} +1 -1
  51. package/src/assets/web-panel/assets/{P2P-CWd90etw.js → P2P-BYpBNYnh.js} +2 -2
  52. package/src/assets/web-panel/assets/{PdhVaultBrowser-DtCGC-W-.js → PdhVaultBrowser-CYCsrrLg.js} +3 -3
  53. package/src/assets/web-panel/assets/{Permissions-CvQ7nr7T.js → Permissions-ZRXGXDFb.js} +4 -4
  54. package/src/assets/web-panel/assets/{PersonalDataHub-rdNPcR-Z.js → PersonalDataHub-6OWUPHyL.js} +5 -5
  55. package/src/assets/web-panel/assets/{Pipeline-CmrfiNOs.js → Pipeline-FcjlDK9E.js} +1 -1
  56. package/src/assets/web-panel/assets/{Privacy-D7ep1h_t.js → Privacy-vD_6Tldp.js} +1 -1
  57. package/src/assets/web-panel/assets/{ProjectInit-C0xKINHS.js → ProjectInit-B_CHOLXp.js} +2 -2
  58. package/src/assets/web-panel/assets/{ProjectSettings-AOmZFp-F.js → ProjectSettings-rEiqQ2da.js} +2 -2
  59. package/src/assets/web-panel/assets/{Projects-BWHSkXZP.js → Projects-Bg8obRAM.js} +1 -1
  60. package/src/assets/web-panel/assets/{Providers-Cb7JCTPL.js → Providers-Dgtt97B4.js} +1 -1
  61. package/src/assets/web-panel/assets/{QrScannerModal-BDMehcXC.js → QrScannerModal-DaGVylZO.js} +1 -1
  62. package/src/assets/web-panel/assets/{QuickAsk-BF6_1nvx.js → QuickAsk-DP2_2x3u.js} +1 -1
  63. package/src/assets/web-panel/assets/{Recommend-BHfT-A28.js → Recommend-D29yj4ki.js} +1 -1
  64. package/src/assets/web-panel/assets/RemoteSession-Ue2wnBLp.js +5 -0
  65. package/src/assets/web-panel/assets/{Reputation-CsWHLEcj.js → Reputation-CrxIHqeC.js} +1 -1
  66. package/src/assets/web-panel/assets/{Row-D6srnWhm.js → Row-BgSzJPCn.js} +1 -1
  67. package/src/assets/web-panel/assets/{RssFeed-Xhr-KyVd.js → RssFeed-AwhYOrP9.js} +3 -3
  68. package/src/assets/web-panel/assets/{Search-vIbIJvwL.js → Search-D2shpEcm.js} +1 -1
  69. package/src/assets/web-panel/assets/{Security-Bzi5Aiko.js → Security-BtKzhTbT.js} +4 -4
  70. package/src/assets/web-panel/assets/{Services-BHtVX52P.js → Services-BY0DsUSm.js} +2 -2
  71. package/src/assets/web-panel/assets/{Skeleton-C0R8B2jf.js → Skeleton-B3Sb0UI9.js} +1 -1
  72. package/src/assets/web-panel/assets/{Skills-DtT6rWOX.js → Skills-Ne5w0bDs.js} +1 -1
  73. package/src/assets/web-panel/assets/{Sla-0B37mxVU.js → Sla-C8VkaP4T.js} +1 -1
  74. package/src/assets/web-panel/assets/{SpeechSettings-0V2jE1Bs.js → SpeechSettings-BQdIEYP9.js} +1 -1
  75. package/src/assets/web-panel/assets/{SyncSettings-CYedRzaj.js → SyncSettings-CwFafrUg.js} +2 -2
  76. package/src/assets/web-panel/assets/Tasks-yJuKoDlR.js +1 -0
  77. package/src/assets/web-panel/assets/{Templates-BKoHztGT.js → Templates-DYn9qCmx.js} +1 -1
  78. package/src/assets/web-panel/assets/{Tenant--17nwVNZ.js → Tenant-DkKReHvn.js} +1 -1
  79. package/src/assets/web-panel/assets/Terminal-r6bd-owt.js +3 -0
  80. package/src/assets/web-panel/assets/{TimelineRenderer-C5jx4AE3.js → TimelineRenderer-Mq2juUC3.js} +1 -1
  81. package/src/assets/web-panel/assets/{Tokens-DYJKYa9t.js → Tokens-BnMw7MRi.js} +1 -1
  82. package/src/assets/web-panel/assets/{Trigger-DChpWiQ9.js → Trigger-CTzKp4o9.js} +1 -1
  83. package/src/assets/web-panel/assets/{Trust-DfWG52EA.js → Trust-BBrSZm9U.js} +1 -1
  84. package/src/assets/web-panel/assets/{UkeySign-CMugYcyA.js → UkeySign-D9fDvEMq.js} +1 -1
  85. package/src/assets/web-panel/assets/{VideoEditing-BOSmrHAd.js → VideoEditing-DUi5ve-0.js} +1 -1
  86. package/src/assets/web-panel/assets/{Wallet-D5g3k3Sa.js → Wallet-C8Fi1P8_.js} +4 -4
  87. package/src/assets/web-panel/assets/{WebAuthn-D-7u4Mkc.js → WebAuthn-s1LppYi2.js} +5 -5
  88. package/src/assets/web-panel/assets/{WorkflowEditor-CFzZMSUJ.js → WorkflowEditor-DJL2C4lj.js} +1 -1
  89. package/src/assets/web-panel/assets/{chat-VbR6l6mc.js → chat-D3F2ndOB.js} +1 -1
  90. package/src/assets/web-panel/assets/{colors-BSZn8EDS.js → colors-B7739-e3.js} +1 -1
  91. package/src/assets/web-panel/assets/{compact-item-y74obMEq.js → compact-item-CcJbFTF4.js} +1 -1
  92. package/src/assets/web-panel/assets/{createContext-C326bjex.js → createContext-CuP_DEFS.js} +1 -1
  93. package/src/assets/web-panel/assets/devWarning-Cj_36GHq.js +1 -0
  94. package/src/assets/web-panel/assets/{hasIn-D_INJQT_.js → hasIn-6YEXfIik.js} +1 -1
  95. package/src/assets/web-panel/assets/{index-DuIkfnTF.js → index-4h3lpWJ3.js} +1 -1
  96. package/src/assets/web-panel/assets/{index-DBkzrpiL.js → index-BIID4XcP.js} +1 -1
  97. package/src/assets/web-panel/assets/{index-B7SrC-On.js → index-BOHUo3aM.js} +1 -1
  98. package/src/assets/web-panel/assets/{index-BnSfnSMP.js → index-BYYSLEjn.js} +1 -1
  99. package/src/assets/web-panel/assets/{index-KF9Zmqln.js → index-Bh4hcNWW.js} +1 -1
  100. package/src/assets/web-panel/assets/{index-C327pLbw.js → index-BhIKnQAW.js} +1 -1
  101. package/src/assets/web-panel/assets/{index-E_v1ZCTP.js → index-Bru_kpZl.js} +1 -1
  102. package/src/assets/web-panel/assets/{index-Bfw2hf2s.js → index-BsdSnHQE.js} +1 -1
  103. package/src/assets/web-panel/assets/{index-jYaogFE8.js → index-BxSqVMGh.js} +1 -1
  104. package/src/assets/web-panel/assets/{index-Dlxh9D8D.js → index-C3ZA_h1j.js} +1 -1
  105. package/src/assets/web-panel/assets/{index-DdyjrCAu.js → index-C3pctdCb.js} +1 -1
  106. package/src/assets/web-panel/assets/{index-abmn_8Yz.js → index-CRuoRapN.js} +1 -1
  107. package/src/assets/web-panel/assets/{index-qMA4GYdQ.js → index-CY0zd_jc.js} +1 -1
  108. package/src/assets/web-panel/assets/index-CezFrAmD.js +1 -0
  109. package/src/assets/web-panel/assets/index-Cxgguez6.js +1 -0
  110. package/src/assets/web-panel/assets/{index-Cr3uuz0C.js → index-CxradkS6.js} +19 -19
  111. package/src/assets/web-panel/assets/{index-DwHSZLXg.js → index-D0K_rFC1.js} +1 -1
  112. package/src/assets/web-panel/assets/{index-CMHqs3Jb.js → index-D0zdPyTD.js} +1 -1
  113. package/src/assets/web-panel/assets/{index-Bnua8IX3.js → index-D3haorUt.js} +1 -1
  114. package/src/assets/web-panel/assets/{index-BY8-nCgs.js → index-D6Ij_2uw.js} +1 -1
  115. package/src/assets/web-panel/assets/{index-FQBsuExX.js → index-DDRlBaDt.js} +1 -1
  116. package/src/assets/web-panel/assets/{index-C-ZCaUJc.js → index-DGsE7MQz.js} +1 -1
  117. package/src/assets/web-panel/assets/{index-BjcWKg1S.js → index-DHvxXXNA.js} +1 -1
  118. package/src/assets/web-panel/assets/{index-GA80ir_u.js → index-DV11jpeO.js} +1 -1
  119. package/src/assets/web-panel/assets/{index-CsovTpx9.js → index-DXdnfDVK.js} +1 -1
  120. package/src/assets/web-panel/assets/{index-nSn2-C4N.js → index-D_75jYWk.js} +1 -1
  121. package/src/assets/web-panel/assets/{index-BJi3UqBt.js → index-DjI9MkId.js} +1 -1
  122. package/src/assets/web-panel/assets/{index-gINyI1rh.js → index-DlSvpNMf.js} +1 -1
  123. package/src/assets/web-panel/assets/{index-CfLJ42kv.js → index-Dox44j29.js} +1 -1
  124. package/src/assets/web-panel/assets/{index-C2NyllnW.js → index-DpEKmjoa.js} +1 -1
  125. package/src/assets/web-panel/assets/{index-Iw0FfqUz.js → index-Dsy7_x4s.js} +1 -1
  126. package/src/assets/web-panel/assets/{index-4eyH-eC2.js → index-Gxw09e9R.js} +1 -1
  127. package/src/assets/web-panel/assets/{index-4PIeTM--.js → index-LfRlSmqe.js} +1 -1
  128. package/src/assets/web-panel/assets/{index-IZAWt0H8.js → index-Nz-MUHD1.js} +1 -1
  129. package/src/assets/web-panel/assets/{index-DUU2606o.js → index-b9cuvFEv.js} +1 -1
  130. package/src/assets/web-panel/assets/{index-CzE53HiB.js → index-eR6H6NR7.js} +1 -1
  131. package/src/assets/web-panel/assets/{index-9PIVuXqo.js → index-fWgUQZ9d.js} +1 -1
  132. package/src/assets/web-panel/assets/{index-DkH1JvEt.js → index-mfWQRSLn.js} +1 -1
  133. package/src/assets/web-panel/assets/{index-DNMxIsDg.js → index-yOZYSNmG.js} +1 -1
  134. package/src/assets/web-panel/assets/{initDefaultProps-DI0Zaf8-.js → initDefaultProps-CJ1Bq2L3.js} +1 -1
  135. package/src/assets/web-panel/assets/{motion-BZDKSy3Z.js → motion-rYJN_vzf.js} +1 -1
  136. package/src/assets/web-panel/assets/{move-DsIW01Vp.js → move-DnDK2gTP.js} +1 -1
  137. package/src/assets/web-panel/assets/{mtc-parser-CohkDhsf.js → mtc-parser-CKnXy7ES.js} +1 -1
  138. package/src/assets/web-panel/assets/{omit-DpiN5rzg.js → omit-3nRc3hWT.js} +1 -1
  139. package/src/assets/web-panel/assets/{pickAttrs-qbuqni3y.js → pickAttrs-Ddjj3sLu.js} +1 -1
  140. package/src/assets/web-panel/assets/{placementArrow-mBky6714.js → placementArrow-CbsbphQY.js} +1 -1
  141. package/src/assets/web-panel/assets/{responsiveObserve-Bas3eQ2K.js → responsiveObserve-D9aZ4VDJ.js} +1 -1
  142. package/src/assets/web-panel/assets/{slide-CBx_SKoK.js → slide-D4pvdFj0.js} +1 -1
  143. package/src/assets/web-panel/assets/{statusUtils-BlYguJSV.js → statusUtils-C2W-vY-S.js} +1 -1
  144. package/src/assets/web-panel/assets/{styleChecker-BCYQmRGv.js → styleChecker-Cbq5150d.js} +1 -1
  145. package/src/assets/web-panel/assets/{useFlexGapSupport-Dp5dybLM.js → useFlexGapSupport-BA2B7R82.js} +1 -1
  146. package/src/assets/web-panel/assets/{useFs-DHAMBgA2.js → useFs-CLrwg3xg.js} +1 -1
  147. package/src/assets/web-panel/assets/{usePersonalDataHub-W6jsSw76.js → usePersonalDataHub-DQeAI5qs.js} +1 -1
  148. package/src/assets/web-panel/assets/{vnode-BkI6aayc.js → vnode-8BDGy-YU.js} +1 -1
  149. package/src/assets/web-panel/assets/{zoom-C_IMGueq.js → zoom-B2SzFprl.js} +1 -1
  150. package/src/assets/web-panel/index.html +1 -1
  151. package/src/commands/agenda.js +74 -1
  152. package/src/commands/background-session.js +11 -1
  153. package/src/commands/batch.js +160 -5
  154. package/src/commands/plugin.js +90 -27
  155. package/src/commands/session.js +31 -1
  156. package/src/commands/team.js +212 -6
  157. package/src/data/changelog.json +26 -0
  158. package/src/gateways/terminal/PtyManager.js +45 -15
  159. package/src/gateways/ws/ws-agent-handler.js +15 -1
  160. package/src/gateways/ws/ws-session-gateway.js +88 -0
  161. package/src/harness/background-task-command-runner.js +234 -0
  162. package/src/harness/background-task-manager.js +150 -20
  163. package/src/harness/background-task-worker.js +17 -11
  164. package/src/harness/jsonl-session-store.js +57 -6
  165. package/src/harness/mcp-client.js +65 -43
  166. package/src/lib/agent-batch.js +4 -1
  167. package/src/lib/agent-team/team-worktree.js +3 -0
  168. package/src/lib/async-hook-supervisor.cjs +82 -10
  169. package/src/lib/async-hook-supervisor.js +50 -4
  170. package/src/lib/background-agent-supervisor.js +46 -0
  171. package/src/lib/collaboration-run-store.js +362 -0
  172. package/src/lib/event-runtime-host.js +12 -10
  173. package/src/lib/hook-runner.cjs +105 -18
  174. package/src/lib/hook-runner.js +79 -6
  175. package/src/lib/hook-shell-command.cjs +148 -0
  176. package/src/lib/hooks-v2-runtime.js +280 -81
  177. package/src/lib/hooks-v2-workspace-context.js +258 -0
  178. package/src/lib/jsonl-session-store.js +2 -0
  179. package/src/lib/lsp/__tests__/lsp-client.test.js +15 -0
  180. package/src/lib/lsp/lsp-client.js +12 -1
  181. package/src/lib/plugin-monitor-supervisor.js +53 -16
  182. package/src/lib/plugin-runtime/bin.js +21 -5
  183. package/src/lib/plugin-runtime/install.js +249 -57
  184. package/src/lib/plugin-runtime/mcp.js +21 -2
  185. package/src/lib/plugin-runtime/monitors.js +19 -3
  186. package/src/lib/plugin-runtime/sandbox-policy.js +100 -4
  187. package/src/lib/process-execution-broker/index.js +1151 -44
  188. package/src/lib/process-execution-broker/linux-generic-bwrap-runtime.js +1557 -0
  189. package/src/lib/process-execution-broker/linux-generic-bwrap.js +1378 -0
  190. package/src/lib/process-execution-broker/platform-sandbox.js +586 -58
  191. package/src/lib/session-usage.js +202 -2
  192. package/src/lib/stream-retry.js +29 -0
  193. package/src/repl/agent-repl.js +40 -3
  194. package/src/runtime/agent-core.js +304 -42
  195. package/src/runtime/headless-runner.js +48 -1
  196. package/src/runtime/headless-stream.js +42 -2
  197. package/src/workers/background-agent-worker.js +5 -11
  198. package/src/assets/web-panel/assets/MarkdownRenderer-C0o4FtpD.js +0 -1
  199. package/src/assets/web-panel/assets/MobileProjects-VTc3gT1y.js +0 -1
  200. package/src/assets/web-panel/assets/RemoteSession-B6EEf8hh.js +0 -5
  201. package/src/assets/web-panel/assets/Tasks-D-R1x8vm.js +0 -1
  202. package/src/assets/web-panel/assets/Terminal-Dnkos7vW.js +0 -3
  203. package/src/assets/web-panel/assets/devWarning-D3Bho_6u.js +0 -1
  204. package/src/assets/web-panel/assets/index-5iAFwYx1.js +0 -1
  205. package/src/assets/web-panel/assets/index-BfnB7LPx.js +0 -1
@@ -0,0 +1,258 @@
1
+ import { AsyncLocalStorage } from "node:async_hooks";
2
+ import { createHash } from "node:crypto";
3
+ import fs from "node:fs";
4
+ import path from "node:path";
5
+
6
+ const trustedWorkspaceStorage = new AsyncLocalStorage();
7
+ const registeredHostWorkspaces = new Map();
8
+ const registeredHostWorkspaceIdentities = new Map();
9
+ const MAX_REGISTERED_HOST_WORKSPACES = 1024;
10
+
11
+ function validateHostWorkspaceRoot(workspaceRoot) {
12
+ if (
13
+ typeof workspaceRoot !== "string" ||
14
+ workspaceRoot.length === 0 ||
15
+ workspaceRoot.includes("\0")
16
+ ) {
17
+ const error = new TypeError(
18
+ "Hooks v2 host workspace root must be a non-empty path",
19
+ );
20
+ error.code = "CC_HOOK_TRUSTED_WORKSPACE_INVALID";
21
+ throw error;
22
+ }
23
+ return path.resolve(workspaceRoot);
24
+ }
25
+
26
+ function invalidWorkspaceRoot(message, cause) {
27
+ const error = new Error(message, cause ? { cause } : undefined);
28
+ error.code = "CC_HOOK_TRUSTED_WORKSPACE_INVALID";
29
+ return error;
30
+ }
31
+
32
+ function workspaceGeneration(stats) {
33
+ // dev+ino alone is insufficient: filesystems may immediately reuse an inode
34
+ // after a same-path directory replacement. Prefer the stable creation
35
+ // timestamp and fail closed on metadata changes when a filesystem does not
36
+ // expose one.
37
+ return stats.birthtimeNs > 0n
38
+ ? `birth:${stats.birthtimeNs.toString()}`
39
+ : `ctime:${stats.ctimeNs.toString()}`;
40
+ }
41
+
42
+ function captureWorkspaceIdentity(workspaceRoot) {
43
+ const resolvedRoot = validateHostWorkspaceRoot(workspaceRoot);
44
+ let canonicalRoot;
45
+ let stats;
46
+ try {
47
+ canonicalRoot = fs.realpathSync.native(resolvedRoot);
48
+ stats = fs.statSync(canonicalRoot, { bigint: true });
49
+ } catch (error) {
50
+ throw invalidWorkspaceRoot(
51
+ "Hooks v2 host workspace root must resolve to an existing directory",
52
+ error,
53
+ );
54
+ }
55
+ if (!stats.isDirectory()) {
56
+ throw invalidWorkspaceRoot(
57
+ "Hooks v2 host workspace root must resolve to a directory",
58
+ );
59
+ }
60
+ return Object.freeze({
61
+ canonicalRoot,
62
+ device: stats.dev,
63
+ inode: stats.ino,
64
+ generation: workspaceGeneration(stats),
65
+ });
66
+ }
67
+
68
+ function workspaceIdentityKey(identity) {
69
+ return JSON.stringify([
70
+ identity.canonicalRoot,
71
+ identity.device.toString(),
72
+ identity.inode.toString(),
73
+ identity.generation,
74
+ ]);
75
+ }
76
+
77
+ function sameWorkspaceIdentity(left, right) {
78
+ return (
79
+ left.canonicalRoot === right.canonicalRoot &&
80
+ left.device === right.device &&
81
+ left.inode === right.inode &&
82
+ left.generation === right.generation
83
+ );
84
+ }
85
+
86
+ function invalidateWorkspaceRecord(record) {
87
+ if (registeredHostWorkspaces.get(record.binding.bindingId) === record) {
88
+ registeredHostWorkspaces.delete(record.binding.bindingId);
89
+ }
90
+ if (
91
+ registeredHostWorkspaceIdentities.get(record.identityKey) ===
92
+ record.binding.bindingId
93
+ ) {
94
+ registeredHostWorkspaceIdentities.delete(record.identityKey);
95
+ }
96
+ }
97
+
98
+ function verifyWorkspaceRecord(record) {
99
+ if (!record) return null;
100
+ if (registeredHostWorkspaces.get(record.binding.bindingId) !== record) {
101
+ return null;
102
+ }
103
+ let currentIdentity;
104
+ try {
105
+ currentIdentity = captureWorkspaceIdentity(record.binding.workspaceRoot);
106
+ } catch {
107
+ invalidateWorkspaceRecord(record);
108
+ return null;
109
+ }
110
+ if (!sameWorkspaceIdentity(record.identity, currentIdentity)) {
111
+ invalidateWorkspaceRecord(record);
112
+ return null;
113
+ }
114
+ return record.binding;
115
+ }
116
+
117
+ function pruneInvalidWorkspaceRecords() {
118
+ for (const record of Array.from(registeredHostWorkspaces.values())) {
119
+ verifyWorkspaceRecord(record);
120
+ }
121
+ }
122
+
123
+ function workspaceBindingId(identity) {
124
+ return createHash("sha256")
125
+ .update("chainlesschain.hooks-v2-host-workspace.v3\0")
126
+ .update(workspaceIdentityKey(identity), "utf8")
127
+ .digest("hex");
128
+ }
129
+
130
+ /**
131
+ * Register a root selected by trusted host bootstrap code. Durable records
132
+ * persist only the returned opaque stable digest; the plaintext canonical path
133
+ * and directory identity remain process-local.
134
+ */
135
+ export function registerHostHooksV2Workspace(workspaceRoot) {
136
+ const identity = captureWorkspaceIdentity(workspaceRoot);
137
+ const identityKey = workspaceIdentityKey(identity);
138
+ const existingBindingId = registeredHostWorkspaceIdentities.get(identityKey);
139
+ if (existingBindingId) {
140
+ const existingRecord = registeredHostWorkspaces.get(existingBindingId);
141
+ const existingBinding = verifyWorkspaceRecord(existingRecord);
142
+ if (
143
+ existingBinding &&
144
+ sameWorkspaceIdentity(existingRecord.identity, identity)
145
+ ) {
146
+ return existingBinding;
147
+ }
148
+ }
149
+
150
+ // A long-running host may create and remove many isolated worktrees. Sweep
151
+ // stale filesystem identities before enforcing the bounded registry so
152
+ // deleted roots cannot exhaust future trusted registrations.
153
+ if (registeredHostWorkspaces.size >= MAX_REGISTERED_HOST_WORKSPACES) {
154
+ pruneInvalidWorkspaceRecords();
155
+ }
156
+ if (registeredHostWorkspaces.size >= MAX_REGISTERED_HOST_WORKSPACES) {
157
+ const error = new Error(
158
+ "Hooks v2 host workspace registry capacity exceeded",
159
+ );
160
+ error.code = "CC_HOOK_TRUSTED_WORKSPACE_LIMIT";
161
+ throw error;
162
+ }
163
+
164
+ const bindingId = workspaceBindingId(identity);
165
+ const collidingRecord = registeredHostWorkspaces.get(bindingId);
166
+ if (collidingRecord) {
167
+ const collidingBinding = verifyWorkspaceRecord(collidingRecord);
168
+ if (
169
+ collidingBinding &&
170
+ sameWorkspaceIdentity(collidingRecord.identity, identity)
171
+ ) {
172
+ registeredHostWorkspaceIdentities.set(identityKey, bindingId);
173
+ return collidingBinding;
174
+ }
175
+ if (
176
+ collidingBinding &&
177
+ collidingRecord.identity.canonicalRoot !== identity.canonicalRoot
178
+ ) {
179
+ const error = new Error("Hooks v2 workspace binding digest collision");
180
+ error.code = "CC_HOOK_TRUSTED_WORKSPACE_COLLISION";
181
+ throw error;
182
+ }
183
+ if (collidingBinding) {
184
+ throw invalidWorkspaceRoot(
185
+ "Hooks v2 host workspace identity changed during registration",
186
+ );
187
+ }
188
+ }
189
+
190
+ const binding = Object.freeze({
191
+ bindingId,
192
+ workspaceRoot: identity.canonicalRoot,
193
+ });
194
+ const record = Object.freeze({
195
+ binding,
196
+ identity,
197
+ identityKey,
198
+ });
199
+ registeredHostWorkspaces.set(bindingId, record);
200
+ registeredHostWorkspaceIdentities.set(identityKey, bindingId);
201
+ return binding;
202
+ }
203
+
204
+ /**
205
+ * Run a CLI-host operation with an immutable Hooks v2 workspace authority.
206
+ *
207
+ * Only host bootstrap code may call this with a root selected before hook,
208
+ * plugin, model, or event payload processing. The binding is async-scoped
209
+ * instead of global so concurrent CLI runs cannot replace one another's
210
+ * writable root.
211
+ */
212
+ export function runWithHostHooksV2Workspace(workspaceRoot, callback) {
213
+ if (typeof callback !== "function") {
214
+ throw new TypeError("Hooks v2 workspace callback must be a function");
215
+ }
216
+ const binding = registerHostHooksV2Workspace(workspaceRoot);
217
+ return trustedWorkspaceStorage.run(
218
+ registeredHostWorkspaces.get(binding.bindingId),
219
+ callback,
220
+ );
221
+ }
222
+
223
+ /**
224
+ * Internal consumer seam for HooksV2Runtime. Event and hook payloads never
225
+ * participate in selecting this value.
226
+ */
227
+ export function currentHostHooksV2WorkspaceRoot() {
228
+ return currentHostHooksV2WorkspaceBinding()?.workspaceRoot || null;
229
+ }
230
+
231
+ export function currentHostHooksV2WorkspaceBinding() {
232
+ return verifyWorkspaceRecord(trustedWorkspaceStorage.getStore());
233
+ }
234
+
235
+ /**
236
+ * Resolve an opaque durable binding only against roots registered by the
237
+ * current host process. No persisted or event-supplied path is accepted.
238
+ */
239
+ export function resolveRegisteredHostHooksV2Workspace(bindingId) {
240
+ if (typeof bindingId !== "string" || !/^[a-f0-9]{64}$/.test(bindingId)) {
241
+ return null;
242
+ }
243
+ return verifyWorkspaceRecord(registeredHostWorkspaces.get(bindingId));
244
+ }
245
+
246
+ /**
247
+ * Revoke a process-local binding owned by trusted host lifecycle code.
248
+ * Payloads, hooks, and durable records must never call this release seam.
249
+ */
250
+ export function releaseRegisteredHostHooksV2Workspace(bindingId) {
251
+ if (typeof bindingId !== "string" || !/^[a-f0-9]{64}$/.test(bindingId)) {
252
+ return false;
253
+ }
254
+ const record = registeredHostWorkspaces.get(bindingId);
255
+ if (!record) return false;
256
+ invalidateWorkspaceRecord(record);
257
+ return true;
258
+ }
@@ -16,6 +16,8 @@ export {
16
16
  appendAssistantMessage,
17
17
  appendToolCall,
18
18
  appendToolCallCompact,
19
+ appendLlmRetryCompact,
20
+ MAX_COMPACT_DURATION_MS,
19
21
  appendToolResult,
20
22
  appendCompactEvent,
21
23
  readEvents,
@@ -100,6 +100,10 @@ describe("LSPClient handshake", () => {
100
100
 
101
101
  it("passes plugin sandbox requirements to ProcessExecutionBroker", async () => {
102
102
  const child = new FakeChild();
103
+ const contract = Object.freeze({ kind: "test-workspace-contract" });
104
+ const issueAuthority = vi
105
+ .spyOn(executionBroker, "issueLinuxWorkspaceSandboxExecutionContract")
106
+ .mockReturnValue(contract);
103
107
  const brokerSpawn = vi
104
108
  .spyOn(executionBroker, "spawn")
105
109
  .mockReturnValue(child);
@@ -123,8 +127,19 @@ describe("LSPClient handshake", () => {
123
127
  sandboxPolicy: {
124
128
  requiredBoundaries: ["filesystem", "network"],
125
129
  },
130
+ sandboxExecutionContract: contract,
126
131
  }),
127
132
  );
133
+ expect(issueAuthority).toHaveBeenCalledWith(
134
+ "fake-lsp",
135
+ ["--stdio"],
136
+ expect.objectContaining({
137
+ cwd: "/proj",
138
+ shell: false,
139
+ origin: "plugin:lsp",
140
+ }),
141
+ "/proj",
142
+ );
128
143
  expect(_deps.spawn).not.toHaveBeenCalled();
129
144
  });
130
145
 
@@ -83,7 +83,7 @@ export class LSPClient extends EventEmitter {
83
83
  const spawnFn = String(this.origin || "").startsWith("plugin:")
84
84
  ? executionBroker.spawn.bind(executionBroker)
85
85
  : _deps.spawn;
86
- this._child = spawnFn(spawnSpec.file, spawnSpec.args, {
86
+ const spawnOptions = {
87
87
  cwd: this.rootPath,
88
88
  env: { ...process.env, ...this.env },
89
89
  stdio: ["pipe", "pipe", "pipe"],
@@ -103,6 +103,17 @@ export class LSPClient extends EventEmitter {
103
103
  : {}),
104
104
  }
105
105
  : {}),
106
+ };
107
+ const sandboxExecutionContract =
108
+ executionBroker.issueLinuxWorkspaceSandboxExecutionContract(
109
+ spawnSpec.file,
110
+ spawnSpec.args,
111
+ spawnOptions,
112
+ this.rootPath,
113
+ );
114
+ this._child = spawnFn(spawnSpec.file, spawnSpec.args, {
115
+ ...spawnOptions,
116
+ ...(sandboxExecutionContract ? { sandboxExecutionContract } : {}),
106
117
  });
107
118
 
108
119
  this._child.on("error", (err) => {
@@ -25,6 +25,7 @@ import { executionBroker } from "./process-execution-broker/index.js";
25
25
  import { EventRuntimeProducer } from "./event-runtime-producer.js";
26
26
  import { EventRuntimeStore } from "./event-runtime-store.js";
27
27
  import { monitorEventId } from "./monitor-event.js";
28
+ import { resolvePluginWorkspaceAuthority } from "./plugin-runtime/sandbox-policy.js";
28
29
 
29
30
  export const _deps = {
30
31
  spawn: (...args) => executionBroker.spawn(...args),
@@ -44,7 +45,9 @@ export class PluginMonitorSupervisor {
44
45
  this._pluginSpawn = opts.brokerSpawn || this._spawn;
45
46
  const eventRuntimeStore =
46
47
  opts.eventRuntimeStore ||
47
- (process.env.CC_EVENT_RUNTIME_DURABLE === "1" ? new EventRuntimeStore() : null);
48
+ (process.env.CC_EVENT_RUNTIME_DURABLE === "1"
49
+ ? new EventRuntimeStore()
50
+ : null);
48
51
  this._runtimeProducer = eventRuntimeStore
49
52
  ? new EventRuntimeProducer({ store: eventRuntimeStore, emitter: this })
50
53
  : null;
@@ -118,7 +121,11 @@ export class PluginMonitorSupervisor {
118
121
  { origin: "monitor", id: monitorEventId(desc.id, { stream, line }) },
119
122
  );
120
123
  } catch (error) {
121
- this._outputs.push({ ...output, stream: "error", line: `durable publish failed: ${error.message}` });
124
+ this._outputs.push({
125
+ ...output,
126
+ stream: "error",
127
+ line: `durable publish failed: ${error.message}`,
128
+ });
122
129
  }
123
130
  if (this._outputs.length > OUTPUT_RING) this._outputs.shift();
124
131
  }
@@ -211,24 +218,54 @@ export class PluginMonitorSupervisor {
211
218
 
212
219
  _spawnForDescriptor(desc, options) {
213
220
  const isPlugin = desc?.origin === "plugin:monitor";
221
+ const pluginWorkspaceRoot = isPlugin
222
+ ? resolvePluginWorkspaceAuthority(desc.pluginWorkspaceAuthority, {
223
+ origin: desc.origin,
224
+ pluginId: desc.pluginId,
225
+ pluginVersion: desc.pluginVersion,
226
+ pluginSource: desc.pluginSource,
227
+ })
228
+ : null;
229
+ if (
230
+ isPlugin &&
231
+ desc.sandboxPolicy?.requiredBoundaries?.length > 0 &&
232
+ !pluginWorkspaceRoot
233
+ ) {
234
+ throw new Error(
235
+ `plugin monitor "${desc.id || desc.name || "unknown"}" is missing its trusted workspace authority`,
236
+ );
237
+ }
238
+ const spawnOptions = {
239
+ ...options,
240
+ origin: isPlugin ? desc.origin : "plugin-monitor:process",
241
+ policy: "allow",
242
+ scope: "plugin-monitor",
243
+ ...(isPlugin
244
+ ? {
245
+ pluginId: desc.pluginId,
246
+ pluginVersion: desc.pluginVersion,
247
+ pluginSource: desc.pluginSource,
248
+ ...(desc.sandboxPolicy
249
+ ? { sandboxPolicy: desc.sandboxPolicy }
250
+ : {}),
251
+ }
252
+ : {}),
253
+ };
254
+ const sandboxExecutionContract =
255
+ isPlugin && pluginWorkspaceRoot
256
+ ? executionBroker.issueLinuxWorkspaceSandboxExecutionContract(
257
+ desc.command,
258
+ desc.args,
259
+ spawnOptions,
260
+ pluginWorkspaceRoot,
261
+ )
262
+ : null;
214
263
  return (isPlugin ? this._pluginSpawn : this._spawn)(
215
264
  desc.command,
216
265
  desc.args,
217
266
  {
218
- ...options,
219
- origin: isPlugin ? desc.origin : "plugin-monitor:process",
220
- policy: "allow",
221
- scope: "plugin-monitor",
222
- ...(isPlugin
223
- ? {
224
- pluginId: desc.pluginId,
225
- pluginVersion: desc.pluginVersion,
226
- pluginSource: desc.pluginSource,
227
- ...(desc.sandboxPolicy
228
- ? { sandboxPolicy: desc.sandboxPolicy }
229
- : {}),
230
- }
231
- : {}),
267
+ ...spawnOptions,
268
+ ...(sandboxExecutionContract ? { sandboxExecutionContract } : {}),
232
269
  },
233
270
  );
234
271
  }
@@ -990,7 +990,20 @@ export function reattestPluginBinInvocation(invocation) {
990
990
  * policy-bearing Plugin Node or native invocation. Object identity is the
991
991
  * capability: callers cannot mint a usable contract by copying public fields.
992
992
  */
993
- export function createPluginSandboxExecutionContract(invocation) {
993
+ export function createPluginSandboxExecutionContract(invocation, options = {}) {
994
+ if (
995
+ options === null ||
996
+ typeof options !== "object" ||
997
+ Array.isArray(options) ||
998
+ Object.keys(options).some((key) => key !== "sync") ||
999
+ (options.sync !== undefined && typeof options.sync !== "boolean")
1000
+ ) {
1001
+ throw pluginBinError(
1002
+ "ERR_PLUGIN_SANDBOX_CONTRACT_OPTIONS_INVALID",
1003
+ "plugin sandbox execution contract options must contain an optional boolean sync field",
1004
+ );
1005
+ }
1006
+ const sync = options.sync !== false;
994
1007
  const issuedResolution =
995
1008
  invocation && typeof invocation === "object"
996
1009
  ? _issuedPluginBinInvocations.get(invocation)
@@ -1118,7 +1131,7 @@ export function createPluginSandboxExecutionContract(invocation) {
1118
1131
  contractVersion: 1,
1119
1132
  kind:
1120
1133
  runtime === "native"
1121
- ? "strict-plugin-native-static-elf-bin"
1134
+ ? "strict-plugin-native-elf-bin"
1122
1135
  : "strict-plugin-node-bin",
1123
1136
  pluginRoot,
1124
1137
  workingDirectory: pluginRoot,
@@ -1140,7 +1153,7 @@ export function createPluginSandboxExecutionContract(invocation) {
1140
1153
  pluginSource: invocation.pluginSource,
1141
1154
  pluginExecutableIdentity: invocation.executableIdentity,
1142
1155
  requiredBoundaries: invocation.sandboxPolicy.requiredBoundaries,
1143
- sync: true,
1156
+ sync,
1144
1157
  }),
1145
1158
  );
1146
1159
  return contract;
@@ -1156,14 +1169,17 @@ export function createPluginSandboxExecutionContract(invocation) {
1156
1169
  /**
1157
1170
  * Backward-compatible Node-only issuer retained for existing callers.
1158
1171
  */
1159
- export function createPluginNodeSandboxExecutionContract(invocation) {
1172
+ export function createPluginNodeSandboxExecutionContract(
1173
+ invocation,
1174
+ options = {},
1175
+ ) {
1160
1176
  if (invocation?.runtime !== "node") {
1161
1177
  throw pluginBinError(
1162
1178
  "ERR_PLUGIN_NODE_SANDBOX_CONTRACT_UNSUPPORTED",
1163
1179
  "plugin Node sandbox execution contract requires one direct policy-bearing Node bin",
1164
1180
  );
1165
1181
  }
1166
- return createPluginSandboxExecutionContract(invocation);
1182
+ return createPluginSandboxExecutionContract(invocation, options);
1167
1183
  }
1168
1184
 
1169
1185
  /**