chainlesschain 0.163.8 → 0.165.1

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 (240) hide show
  1. package/package.json +7 -4
  2. package/scripts/macos-mcp-launcher-build.mjs +317 -0
  3. package/scripts/macos-mcp-launcher-postinstall.sh +54 -0
  4. package/scripts/macos-mcp-launcher-preinstall.sh +61 -0
  5. package/src/assets/web-panel/.build-hash +1 -1
  6. package/src/assets/web-panel/assets/{AIOps-CavM7W3k.js → AIOps-CYxFDrcm.js} +1 -1
  7. package/src/assets/web-panel/assets/{ActionButton-CUvZGabQ.js → ActionButton-CPBtdiU2.js} +1 -1
  8. package/src/assets/web-panel/assets/{Analytics-DeBnWnbe.js → Analytics-Dayq7Dp7.js} +3 -3
  9. package/src/assets/web-panel/assets/{AppLayout-DbI5kxvd.js → AppLayout-6U7bPb2s.js} +4 -4
  10. package/src/assets/web-panel/assets/{Artifacts-D93bigKm.js → Artifacts-D2Iqg_3D.js} +1 -1
  11. package/src/assets/web-panel/assets/{Audit-CBNvfUZa.js → Audit-CdoOB7WV.js} +1 -1
  12. package/src/assets/web-panel/assets/{BackgroundAgents-CJLyh_YU.js → BackgroundAgents-DrnAVB8w.js} +1 -1
  13. package/src/assets/web-panel/assets/{Backup-D_0yoehD.js → Backup-CesMi5cy.js} +1 -1
  14. package/src/assets/web-panel/assets/{BaseInput-TJxmbObW.js → BaseInput-DaX9ptax.js} +1 -1
  15. package/src/assets/web-panel/assets/{Chat-CE0dto1q.js → Chat-OrwOPxDS.js} +5 -5
  16. package/src/assets/web-panel/assets/{ChatBubbleRenderer-CZ5-yYiD.js → ChatBubbleRenderer-CvPZFRxM.js} +1 -1
  17. package/src/assets/web-panel/assets/{Checkbox-Ck1RVIVo.js → Checkbox-DiHbcysC.js} +1 -1
  18. package/src/assets/web-panel/assets/{Codegen-qpoyf6DK.js → Codegen-CbgCkaBJ.js} +1 -1
  19. package/src/assets/web-panel/assets/{Col-DUyPWjIE.js → Col-Du5hgrh4.js} +1 -1
  20. package/src/assets/web-panel/assets/{Community-D_S1BemS.js → Community-DsPOBW-p.js} +1 -1
  21. package/src/assets/web-panel/assets/{Compact-BjxeK-Gc.js → Compact-BwHIFkNC.js} +1 -1
  22. package/src/assets/web-panel/assets/{Compliance-Dd3CEJsu.js → Compliance-BvrDQSgd.js} +1 -1
  23. package/src/assets/web-panel/assets/{Cowork-B4QG7-ad.js → Cowork-gwAE6arx.js} +3 -3
  24. package/src/assets/web-panel/assets/{Cron-BtZnids5.js → Cron-DW-0l4uF.js} +2 -2
  25. package/src/assets/web-panel/assets/{Crosschain-CHPfCBFD.js → Crosschain-j-NsLesB.js} +1 -1
  26. package/src/assets/web-panel/assets/{DID-DT8DCEOK.js → DID-CibVEBOI.js} +2 -2
  27. package/src/assets/web-panel/assets/{Dashboard-COQab5_U.js → Dashboard-W4z9vSfe.js} +2 -2
  28. package/src/assets/web-panel/assets/{Dropdown-BY1PsQuU.js → Dropdown-DAcrkKxG.js} +1 -1
  29. package/src/assets/web-panel/assets/{EmailListRenderer-CsuAuaq3.js → EmailListRenderer-6GXah-89.js} +1 -1
  30. package/src/assets/web-panel/assets/{FamilyGuardDashboard-BqpinD-j.js → FamilyGuardDashboard-TKDCq8Ku.js} +1 -1
  31. package/src/assets/web-panel/assets/{Federation-D0gkfQFv.js → Federation-BXBLlcNm.js} +1 -1
  32. package/src/assets/web-panel/assets/{FormItemContext-BqgE-axK.js → FormItemContext-BoJJBkG3.js} +1 -1
  33. package/src/assets/web-panel/assets/{GenericCardRenderer-D0hJ2KvC.js → GenericCardRenderer-okjsl46v.js} +1 -1
  34. package/src/assets/web-panel/assets/{Git-CmMB0uPY.js → Git-Rvs7i6nn.js} +2 -2
  35. package/src/assets/web-panel/assets/{Governance-BnVEI0Dk.js → Governance-CHSUxXXS.js} +1 -1
  36. package/src/assets/web-panel/assets/{Inference-YTAMihRP.js → Inference-V3BlfxsB.js} +1 -1
  37. package/src/assets/web-panel/assets/{KnowledgeGraph-0ax7VHop.js → KnowledgeGraph-C_NlD4aR.js} +1 -1
  38. package/src/assets/web-panel/assets/{Logs-B9TRcokL.js → Logs-Nn4LNxhA.js} +2 -2
  39. package/src/assets/web-panel/assets/MarkdownRenderer-SVAgNnkt.js +1 -0
  40. package/src/assets/web-panel/assets/{Marketplace-B6CSuD_H.js → Marketplace-DEmjFcg-.js} +1 -1
  41. package/src/assets/web-panel/assets/{McpTools-CjMXvOVU.js → McpTools-DlopXipI.js} +3 -3
  42. package/src/assets/web-panel/assets/{Memory-CXRF71U3.js → Memory-BG3ynX60.js} +2 -2
  43. package/src/assets/web-panel/assets/{MobileBridge-DQgNp_oS.js → MobileBridge-DeRzWOfT.js} +1 -1
  44. package/src/assets/web-panel/assets/{MobileProjects-DlPwcCbq.js → MobileProjects-Tyd3o2A0.js} +1 -1
  45. package/src/assets/web-panel/assets/{Mtc-6hDTRPe1.js → Mtc-BoPbOv1C.js} +2 -2
  46. package/src/assets/web-panel/assets/{MtcAudit-BlVHU1DS.js → MtcAudit-un-rTHp7.js} +2 -2
  47. package/src/assets/web-panel/assets/{Multisig-DP9pzEbV.js → Multisig-Ds0oveEB.js} +3 -3
  48. package/src/assets/web-panel/assets/{NLProgramming-Djorh8EV.js → NLProgramming-D09JI-yY.js} +1 -1
  49. package/src/assets/web-panel/assets/{Notes-oLZ-JkUg.js → Notes-DTRhiKJn.js} +4 -4
  50. package/src/assets/web-panel/assets/{NotificationSettings-Cg288VH3.js → NotificationSettings-BAnnNCim.js} +1 -1
  51. package/src/assets/web-panel/assets/OrderTableRenderer-Yr-1fSYi.js +1 -0
  52. package/src/assets/web-panel/assets/{Organization-CBdo-bHs.js → Organization-S9EAolpj.js} +4 -4
  53. package/src/assets/web-panel/assets/{Overflow-qZdnVumx.js → Overflow-CqcLKqJ8.js} +1 -1
  54. package/src/assets/web-panel/assets/{P2P-D4kNfGNQ.js → P2P-Dw0F9EcU.js} +2 -2
  55. package/src/assets/web-panel/assets/{PdhVaultBrowser-DnlMemM7.js → PdhVaultBrowser-BDRwUevN.js} +5 -5
  56. package/src/assets/web-panel/assets/{Permissions-DHucHFBJ.js → Permissions-D3_c3nKB.js} +4 -4
  57. package/src/assets/web-panel/assets/{PersonalDataHub-BovlOIPR.js → PersonalDataHub-CxdMV98x.js} +4 -4
  58. package/src/assets/web-panel/assets/{Pipeline-Deru3CQY.js → Pipeline-CgjNQFLI.js} +1 -1
  59. package/src/assets/web-panel/assets/{Privacy-Bq5oTTbU.js → Privacy-BHghIIht.js} +1 -1
  60. package/src/assets/web-panel/assets/{ProjectInit-BfzxVWAz.js → ProjectInit-De6pqaKM.js} +2 -2
  61. package/src/assets/web-panel/assets/{ProjectSettings-CKC6XBtH.js → ProjectSettings-Bg4ZkNYQ.js} +2 -2
  62. package/src/assets/web-panel/assets/Projects-CP8vPvzs.js +1 -0
  63. package/src/assets/web-panel/assets/{Providers-C_jx4TWX.js → Providers-CUnHbNf-.js} +1 -1
  64. package/src/assets/web-panel/assets/{QrScannerModal-BdIHeVGC.js → QrScannerModal-DA2f3Wts.js} +1 -1
  65. package/src/assets/web-panel/assets/{QuickAsk-BwZwAcPD.js → QuickAsk-C1FeHDqp.js} +1 -1
  66. package/src/assets/web-panel/assets/{Recommend-BNTt0L-m.js → Recommend-B5guaUMN.js} +1 -1
  67. package/src/assets/web-panel/assets/RemoteSession-PDADSds3.js +5 -0
  68. package/src/assets/web-panel/assets/{Reputation-D6VpG32B.js → Reputation-eTlRhgvK.js} +1 -1
  69. package/src/assets/web-panel/assets/{Row-D_74OvQQ.js → Row-D7AqSGY0.js} +1 -1
  70. package/src/assets/web-panel/assets/{RssFeed-BTr0a-BQ.js → RssFeed-JiuLm4Ja.js} +3 -3
  71. package/src/assets/web-panel/assets/{Search-C3MCFoNU.js → Search-CwruahW3.js} +1 -1
  72. package/src/assets/web-panel/assets/{Security-CqW9aJ6s.js → Security-CdhqaR0b.js} +4 -4
  73. package/src/assets/web-panel/assets/{Services-Cw5GNaHF.js → Services-CU0ZIlcT.js} +2 -2
  74. package/src/assets/web-panel/assets/{Skeleton-DZps5qeY.js → Skeleton-CtduPfAb.js} +1 -1
  75. package/src/assets/web-panel/assets/{Skills-C6NQ1znT.js → Skills-CROXm6AY.js} +1 -1
  76. package/src/assets/web-panel/assets/{Sla-DFP8G2Sh.js → Sla-DiZiABFD.js} +1 -1
  77. package/src/assets/web-panel/assets/{SpeechSettings-ESt6MB6v.js → SpeechSettings-B5pLcXUw.js} +1 -1
  78. package/src/assets/web-panel/assets/{SyncSettings-BHnN98aM.js → SyncSettings-DBX6J1Oe.js} +2 -2
  79. package/src/assets/web-panel/assets/{Tasks-NiSCaOf-.js → Tasks-BLyRJd3-.js} +1 -1
  80. package/src/assets/web-panel/assets/{Templates-DsKFhW2C.js → Templates-BKa5qzmk.js} +1 -1
  81. package/src/assets/web-panel/assets/{Tenant-CQ0E0ze5.js → Tenant-DnD3a4M6.js} +1 -1
  82. package/src/assets/web-panel/assets/{Terminal-D_rBu_by.js → Terminal-DEitQeTb.js} +2 -2
  83. package/src/assets/web-panel/assets/{TimelineRenderer-D3Vm4Po4.js → TimelineRenderer-CjHggTDq.js} +1 -1
  84. package/src/assets/web-panel/assets/{Tokens-DNTxRH6q.js → Tokens-CsGT7sfl.js} +1 -1
  85. package/src/assets/web-panel/assets/{Trigger-Dgprz1ms.js → Trigger-yr4rFDrW.js} +1 -1
  86. package/src/assets/web-panel/assets/{Trust-B2rUnVBD.js → Trust-CLvu1Sjp.js} +1 -1
  87. package/src/assets/web-panel/assets/{UkeySign-DEGQNc6_.js → UkeySign-B0m7yUP8.js} +1 -1
  88. package/src/assets/web-panel/assets/{VideoEditing-jlsAx3Eh.js → VideoEditing-C0VvP4lc.js} +1 -1
  89. package/src/assets/web-panel/assets/{Wallet-8jQih1ij.js → Wallet-kllBAG1M.js} +4 -4
  90. package/src/assets/web-panel/assets/{WebAuthn-XLeO6xIB.js → WebAuthn-C54UQNLx.js} +3 -3
  91. package/src/assets/web-panel/assets/{WorkflowEditor-De_dgDAG.js → WorkflowEditor-B7y8qSvU.js} +1 -1
  92. package/src/assets/web-panel/assets/{chat-CI19R_Sk.js → chat-DRzTh8CJ.js} +1 -1
  93. package/src/assets/web-panel/assets/{colors-DsYTOYN3.js → colors-BSMwSnCx.js} +1 -1
  94. package/src/assets/web-panel/assets/{compact-item-CM3-t_Qr.js → compact-item-BTg602H5.js} +1 -1
  95. package/src/assets/web-panel/assets/{createContext-BjxlBRZv.js → createContext-B6EGsdvp.js} +1 -1
  96. package/src/assets/web-panel/assets/devWarning-Dol8jLEP.js +1 -0
  97. package/src/assets/web-panel/assets/{hasIn-RDaWX_4i.js → hasIn-DDKqJ8RD.js} +1 -1
  98. package/src/assets/web-panel/assets/{index-nAH87mQP.js → index-323Hhxxt.js} +1 -1
  99. package/src/assets/web-panel/assets/{index-DIUnNOjG.js → index-BICn3Qh1.js} +1 -1
  100. package/src/assets/web-panel/assets/{index-DyIWssPC.js → index-BMuGd2--.js} +1 -1
  101. package/src/assets/web-panel/assets/{index-DX7tuWVJ.js → index-BRqt4fO-.js} +1 -1
  102. package/src/assets/web-panel/assets/{index-Cd3euP_4.js → index-BVTAl5N2.js} +1 -1
  103. package/src/assets/web-panel/assets/{index-Cpqm1qfU.js → index-BsR7Y2NG.js} +1 -1
  104. package/src/assets/web-panel/assets/{index-DcPFB8qJ.js → index-Bx044qDj.js} +1 -1
  105. package/src/assets/web-panel/assets/{index-Cd9OJhHG.js → index-C1GFfuqN.js} +1 -1
  106. package/src/assets/web-panel/assets/index-C3QX5tOl.js +1 -0
  107. package/src/assets/web-panel/assets/{index-B-Z8eFUJ.js → index-C66NpWy5.js} +1 -1
  108. package/src/assets/web-panel/assets/{index-BlT2VAEK.js → index-CDTvsX8t.js} +1 -1
  109. package/src/assets/web-panel/assets/{index-Dsij_KsN.js → index-CGNklbP6.js} +1 -1
  110. package/src/assets/web-panel/assets/{index-C4gkzxkw.js → index-CHfLYwcW.js} +1 -1
  111. package/src/assets/web-panel/assets/{index-D6sm5-Xq.js → index-CNxg61YP.js} +1 -1
  112. package/src/assets/web-panel/assets/{index-C0yx-jtQ.js → index-CQlHFN_g.js} +1 -1
  113. package/src/assets/web-panel/assets/{index-CjTrRt13.js → index-CUEU48o1.js} +1 -1
  114. package/src/assets/web-panel/assets/{index-B7WBt_4T.js → index-CWGax2QW.js} +1 -1
  115. package/src/assets/web-panel/assets/{index-IeiL9RKc.js → index-C_JzIXUq.js} +1 -1
  116. package/src/assets/web-panel/assets/{index-CnHH7bnD.js → index-Cf23wVMs.js} +1 -1
  117. package/src/assets/web-panel/assets/{index-BKJr-TZp.js → index-CmAInUkX.js} +1 -1
  118. package/src/assets/web-panel/assets/{index-CiECJi-Q.js → index-CmAubjDi.js} +1 -1
  119. package/src/assets/web-panel/assets/{index-BCnZyIMA.js → index-Co7oM1qr.js} +1 -1
  120. package/src/assets/web-panel/assets/{index-Drlnwr8F.js → index-CwPUZbMI.js} +1 -1
  121. package/src/assets/web-panel/assets/{index-9R6BxAr0.js → index-D1naNj4U.js} +1 -1
  122. package/src/assets/web-panel/assets/{index-BGqqJfL0.js → index-D5CsBShd.js} +1 -1
  123. package/src/assets/web-panel/assets/{index-Bi46Xu4u.js → index-DBT3YeQ9.js} +1 -1
  124. package/src/assets/web-panel/assets/{index-DXXtGMUJ.js → index-DLdrHhML.js} +1 -1
  125. package/src/assets/web-panel/assets/{index-B0PMlbMs.js → index-Dc77VR3H.js} +1 -1
  126. package/src/assets/web-panel/assets/{index-D0zcy54x.js → index-Dfv2lKu3.js} +1 -1
  127. package/src/assets/web-panel/assets/{index-BN6UPD2E.js → index-DiW0sDkz.js} +1 -1
  128. package/src/assets/web-panel/assets/{index-Dxge-xWl.js → index-DpTHIRfZ.js} +1 -1
  129. package/src/assets/web-panel/assets/{index-CyOGN6a5.js → index-DqFXGzzC.js} +1 -1
  130. package/src/assets/web-panel/assets/{index-B9O9YyWE.js → index-XFe-1twF.js} +1 -1
  131. package/src/assets/web-panel/assets/{index-BHNYPO0b.js → index-p2HYn89m.js} +3 -3
  132. package/src/assets/web-panel/assets/{index-CVQMeiwH.js → index-pqfux8vZ.js} +1 -1
  133. package/src/assets/web-panel/assets/{index-pPL66J85.js → index-rjzk9z1Q.js} +1 -1
  134. package/src/assets/web-panel/assets/{index-DoQA0-52.js → index-th2ihDc0.js} +1 -1
  135. package/src/assets/web-panel/assets/{index-B5REnKt8.js → index-u2YVld9W.js} +1 -1
  136. package/src/assets/web-panel/assets/index-xYybvEic.js +1 -0
  137. package/src/assets/web-panel/assets/{initDefaultProps-BTL88of4.js → initDefaultProps-C3Djh9fa.js} +1 -1
  138. package/src/assets/web-panel/assets/{motion-BUB2pXwB.js → motion-BzqFmbSe.js} +1 -1
  139. package/src/assets/web-panel/assets/{move-egCkkOMm.js → move-DJo_FFaW.js} +1 -1
  140. package/src/assets/web-panel/assets/{mtc-parser-DAWhZC8c.js → mtc-parser-_tlTiBvk.js} +1 -1
  141. package/src/assets/web-panel/assets/{omit-CvOxe_6d.js → omit-CB2ZyxUg.js} +1 -1
  142. package/src/assets/web-panel/assets/{pickAttrs-DZUSLZ5Z.js → pickAttrs-DZwIFD4u.js} +1 -1
  143. package/src/assets/web-panel/assets/{placementArrow-BERxGNMY.js → placementArrow-DxZY0dgk.js} +1 -1
  144. package/src/assets/web-panel/assets/{responsiveObserve-DZuDfZ9n.js → responsiveObserve-CzzGt7GL.js} +1 -1
  145. package/src/assets/web-panel/assets/{slide-DO756sgI.js → slide-BaIK52yz.js} +1 -1
  146. package/src/assets/web-panel/assets/{statusUtils-BRQlIz28.js → statusUtils-3-FHu9_M.js} +1 -1
  147. package/src/assets/web-panel/assets/{styleChecker-DCSfdybl.js → styleChecker-BkRT68fK.js} +1 -1
  148. package/src/assets/web-panel/assets/{useFlexGapSupport-LD8dWVve.js → useFlexGapSupport-DHYwKS-s.js} +1 -1
  149. package/src/assets/web-panel/assets/{useFs-BX9uW2LK.js → useFs-DVVUhV8p.js} +1 -1
  150. package/src/assets/web-panel/assets/{usePersonalDataHub-BPSlPuHU.js → usePersonalDataHub-CkrFKAzQ.js} +1 -1
  151. package/src/assets/web-panel/assets/{vnode-BmOILC9V.js → vnode-BuBLFz0c.js} +1 -1
  152. package/src/assets/web-panel/assets/{zoom-1iKowKGg.js → zoom-kpm6LtXU.js} +1 -1
  153. package/src/assets/web-panel/index.html +1 -1
  154. package/src/command-help-index.json +3 -3
  155. package/src/commands/cowork.js +418 -41
  156. package/src/commands/permissions.js +240 -16
  157. package/src/commands/plugin.js +1293 -56
  158. package/src/commands/session-location.js +276 -0
  159. package/src/commands/session.js +2 -0
  160. package/src/data/changelog.json +28 -2
  161. package/src/gateways/ws/action-protocol.js +316 -17
  162. package/src/gateways/ws/message-dispatcher.js +30 -0
  163. package/src/gateways/ws/remote-session-protocol.js +565 -35
  164. package/src/gateways/ws/ws-agent-handler.js +12 -0
  165. package/src/gateways/ws/ws-approval-gate.js +5 -0
  166. package/src/gateways/ws/ws-server.js +510 -50
  167. package/src/harness/jsonl-session-store.js +341 -24
  168. package/src/harness/mcp-client.js +8 -1
  169. package/src/harness/remote-session-audit.js +2 -0
  170. package/src/harness/remote-session-crypto.js +929 -21
  171. package/src/harness/remote-session-registry.js +945 -24
  172. package/src/harness/session-list-index.js +233 -14
  173. package/src/lib/agent-authority.js +6 -0
  174. package/src/lib/auto-mode-config.js +98 -3
  175. package/src/lib/background-agent-keeper-client.js +248 -0
  176. package/src/lib/background-agent-keeper-protocol.js +291 -0
  177. package/src/lib/background-agent-supervisor.js +1357 -85
  178. package/src/lib/background-interaction-resolver.js +12 -0
  179. package/src/lib/background-session-transport.js +23 -6
  180. package/src/lib/background-turn-bootstrap-protocol.js +190 -0
  181. package/src/lib/cowork-workflow.js +1188 -76
  182. package/src/lib/delivery-production-adapter.js +38 -2
  183. package/src/lib/dynamic-workflow-facade.js +1736 -0
  184. package/src/lib/execution-location-contract.js +551 -0
  185. package/src/lib/execution-location-runtime.js +130 -0
  186. package/src/lib/ide-context.js +94 -6
  187. package/src/lib/mcp-stdio-package-materialization.js +166 -14
  188. package/src/lib/packer/pkg-config-generator.js +2 -0
  189. package/src/lib/permission-authority.js +77 -0
  190. package/src/lib/permission-side-effect-center.js +495 -0
  191. package/src/lib/plugin-runtime/install.js +514 -13
  192. package/src/lib/plugin-runtime/marketplace-artifact-readback.js +900 -0
  193. package/src/lib/plugin-runtime/marketplace-catalog.js +1181 -0
  194. package/src/lib/plugin-runtime/marketplace-impact.js +408 -0
  195. package/src/lib/plugin-runtime/marketplace-remote-artifacts.js +1567 -0
  196. package/src/lib/plugin-security.js +60 -3
  197. package/src/lib/process-execution-broker/index.js +1095 -57
  198. package/src/lib/process-execution-broker/linux-bwrap-descriptor-launch.js +375 -0
  199. package/src/lib/process-execution-broker/linux-generic-bwrap-runtime.js +90 -16
  200. package/src/lib/process-execution-broker/linux-generic-bwrap.js +159 -18
  201. package/src/lib/process-execution-broker/macos-mcp-launcher-contract.js +278 -0
  202. package/src/lib/process-execution-broker/macos-mcp-launcher-protocol.json +23 -0
  203. package/src/lib/process-execution-broker/macos-mcp-launcher.c +1497 -0
  204. package/src/lib/process-execution-broker/mcp-fd-entry-bootstrap.js +37 -0
  205. package/src/lib/process-execution-broker/platform-sandbox.js +2330 -279
  206. package/src/lib/process-execution-broker/windows-sandbox-helper.dll +0 -0
  207. package/src/lib/process-execution-broker/windows-sandbox-helper.exe +0 -0
  208. package/src/lib/process-execution-broker/windows-sandbox.cs +50 -4
  209. package/src/lib/public-ide-capability-manifest.js +3 -0
  210. package/src/lib/remote-approval-bridge.js +777 -34
  211. package/src/lib/remote-control.js +2 -0
  212. package/src/lib/remote-membership-authority.js +632 -0
  213. package/src/lib/remote-membership-coordinator.js +2973 -0
  214. package/src/lib/remote-membership-host-store.js +1799 -0
  215. package/src/lib/runtime-provenance-ledger.js +171 -46
  216. package/src/lib/scoped-permission-store.js +369 -0
  217. package/src/lib/session-anti-rollback-anchor.js +262 -5
  218. package/src/lib/shell-approval.js +169 -6
  219. package/src/lib/sub-agent-context.js +4 -0
  220. package/src/lib/with-file-lock.js +73 -38
  221. package/src/lib/workflow-definition-contract.js +149 -0
  222. package/src/repl/agent-repl.js +111 -36
  223. package/src/repl/chat-repl.js +2 -0
  224. package/src/repl/clipboard-image.js +735 -16
  225. package/src/repl/ide-status.js +1 -1
  226. package/src/repl/prompt-interactions.js +59 -6
  227. package/src/repl/remote-approval.js +29 -7
  228. package/src/runtime/agent-core.js +1182 -155
  229. package/src/runtime/headless-runner.js +52 -30
  230. package/src/runtime/headless-stream.js +51 -28
  231. package/src/workers/background-agent-keeper.js +689 -0
  232. package/src/workers/background-agent-worker.js +365 -13
  233. package/src/workers/background-turn-bootstrap.js +170 -0
  234. package/src/assets/web-panel/assets/MarkdownRenderer-yySN5YNM.js +0 -1
  235. package/src/assets/web-panel/assets/OrderTableRenderer-Bb1O3Ofr.js +0 -1
  236. package/src/assets/web-panel/assets/Projects-CMolGhae.js +0 -1
  237. package/src/assets/web-panel/assets/RemoteSession-Biyn2-Ro.js +0 -5
  238. package/src/assets/web-panel/assets/devWarning-S1ld2LwU.js +0 -1
  239. package/src/assets/web-panel/assets/index-Drz6yq0m.js +0 -1
  240. package/src/assets/web-panel/assets/index-uWiUs7S_.js +0 -1
@@ -0,0 +1,900 @@
1
+ /**
2
+ * Truthful, local readback of marketplace artifact expectations.
3
+ *
4
+ * Registry metadata remains an unverified assertion. This projection compares
5
+ * only fields whose semantics are exact: manifest bytes, manifest license,
6
+ * the cryptographically verified signing key, and the repository-defined
7
+ * payload SBOM format. Persisted remote-artifact evidence is checked for
8
+ * internal consistency and bound back to the installed signature lock;
9
+ * external SBOM bytes are not persisted or re-fetched, so their install-time
10
+ * digest record remains explicitly distinct from a current payload comparison.
11
+ */
12
+
13
+ import crypto from "node:crypto";
14
+ import fs from "node:fs";
15
+ import path from "node:path";
16
+ import { parsePluginManifest } from "./manifest.js";
17
+ import {
18
+ LOCK_FILENAME,
19
+ readPluginLock,
20
+ verifyInstalledSignature,
21
+ } from "./signature.js";
22
+
23
+ export const PLUGIN_MARKETPLACE_ARTIFACT_READBACK_SCHEMA =
24
+ "cc-plugin-marketplace-artifact-readback/v1";
25
+ export const PLUGIN_MARKETPLACE_PAYLOAD_SBOM_SCHEMA =
26
+ "cc-plugin-marketplace-payload-sbom/v1";
27
+ export const MAX_MARKETPLACE_READBACK_FILES = 10_000;
28
+ export const MAX_MARKETPLACE_READBACK_FILE_BYTES = 64 * 1024 * 1024;
29
+ export const MAX_MARKETPLACE_READBACK_TOTAL_BYTES = 1024 * 1024 * 1024;
30
+
31
+ const SOURCE_METADATA_FILENAME = ".plugin-source.json";
32
+ const SHA256_RE = /^[a-f0-9]{64}$/;
33
+ const COMPARABLE_SBOM_FORMATS = new Set([
34
+ "cc-plugin-marketplace-payload-sbom/v1",
35
+ "cc-plugin-payload-sbom/v1",
36
+ ]);
37
+
38
+ export function buildPluginMarketplaceArtifactReadback({
39
+ root,
40
+ scope = null,
41
+ source = null,
42
+ observedAt = new Date().toISOString(),
43
+ } = {}) {
44
+ const resolvedRoot = path.resolve(String(root || ""));
45
+ if (!root || !fs.existsSync(resolvedRoot)) {
46
+ throw new Error("marketplace artifact readback root does not exist");
47
+ }
48
+ const manifest = parsePluginManifest(resolvedRoot);
49
+ if (!manifest.ok || !manifest.manifestPath) {
50
+ throw new Error(
51
+ `marketplace artifact readback manifest is invalid: ${manifest.errors.join("; ")}`,
52
+ );
53
+ }
54
+ const manifestBytes = fs.readFileSync(manifest.manifestPath);
55
+ const manifestSha256 = sha256(manifestBytes);
56
+ const signature = verifyInstalledSignature({ root: resolvedRoot });
57
+ const lock = readPluginLock(resolvedRoot);
58
+ const payloadSbom = buildMarketplacePayloadSbom(resolvedRoot);
59
+ const expectations = normalizeExpectations(
60
+ source?.catalogAuthority?.artifactExpectations,
61
+ );
62
+ const expectedRegistryOrigin = registryOriginFromSource(source);
63
+ const remoteArtifactEvidence = validateRemoteArtifactEvidence(
64
+ source?.catalogAuthority?.remoteArtifactEvidence,
65
+ );
66
+ const actual = {
67
+ manifest: {
68
+ relativePath: path
69
+ .relative(resolvedRoot, manifest.manifestPath)
70
+ .replace(/\\/g, "/"),
71
+ sha256: manifestSha256,
72
+ },
73
+ license: {
74
+ expression: manifest.metadata.license || null,
75
+ authority: "installed-plugin-manifest",
76
+ },
77
+ signature: {
78
+ present: Boolean(lock?.signatureBase64 && lock?.publicKeyPem),
79
+ verified: signature.signed === true,
80
+ algorithm: signature.signed ? "ed25519" : null,
81
+ publicKeySha256: signature.publicKeySha256 || null,
82
+ publicKeyDocumentSha256: hashLockedPublicKeyDocument(lock),
83
+ reason: signature.signed ? null : signature.reason || "unsigned",
84
+ },
85
+ signatureBoundComponentSbom: {
86
+ present: Boolean(lock?.sbom),
87
+ digest: cleanDigest(lock?.sbom?.digest),
88
+ verified:
89
+ signature.signed === true &&
90
+ Boolean(lock?.sbom) &&
91
+ cleanDigest(lock?.sbom?.digest) != null,
92
+ },
93
+ payloadSbom,
94
+ };
95
+ actual.remoteArtifacts = summarizeRemoteArtifacts({
96
+ evidence: remoteArtifactEvidence,
97
+ lock,
98
+ signature: actual.signature,
99
+ expectedRegistryOrigin,
100
+ });
101
+ const comparisons = {
102
+ manifest: compareScalarExpectation(
103
+ expectations.manifest.status,
104
+ expectations.manifest.sha256,
105
+ actual.manifest.sha256,
106
+ ),
107
+ license: compareScalarExpectation(
108
+ expectations.license.status,
109
+ expectations.license.expression,
110
+ actual.license.expression,
111
+ ),
112
+ signature: compareSignature(expectations.signature, actual.signature),
113
+ sbom: compareSbom(expectations.sbom, actual.payloadSbom),
114
+ remoteSignature: compareRemoteSignature(
115
+ expectations.signature,
116
+ actual.remoteArtifacts.signature,
117
+ ),
118
+ remoteSbom: compareRemoteSbom(
119
+ expectations.sbom,
120
+ actual.remoteArtifacts.sbom,
121
+ ),
122
+ };
123
+ const blockers = [];
124
+ if (comparisons.manifest.status === "mismatch") {
125
+ blockers.push(issue("MANIFEST_DIGEST_MISMATCH"));
126
+ }
127
+ if (comparisons.license.status === "mismatch") {
128
+ blockers.push(issue("LICENSE_MISMATCH"));
129
+ }
130
+ if (comparisons.signature.status === "mismatch") {
131
+ blockers.push(
132
+ issue("SIGNATURE_NOT_VERIFIED", actual.signature.reason || null),
133
+ );
134
+ }
135
+ if (comparisons.sbom.status === "mismatch") {
136
+ blockers.push(issue("PAYLOAD_SBOM_DIGEST_MISMATCH"));
137
+ }
138
+ if (remoteArtifactEvidence.present && !remoteArtifactEvidence.valid) {
139
+ blockers.push(issue("REMOTE_ARTIFACT_EVIDENCE_INVALID"));
140
+ }
141
+ if (
142
+ actual.remoteArtifacts.registryOriginComparable &&
143
+ !actual.remoteArtifacts.registryOriginMatches
144
+ ) {
145
+ blockers.push(issue("REMOTE_ARTIFACT_REGISTRY_ORIGIN_MISMATCH"));
146
+ }
147
+ if (comparisons.remoteSignature.status === "mismatch") {
148
+ blockers.push(issue("REMOTE_SIGNATURE_EVIDENCE_MISMATCH"));
149
+ }
150
+ if (comparisons.remoteSbom.status === "mismatch") {
151
+ blockers.push(issue("REMOTE_SBOM_EVIDENCE_MISMATCH"));
152
+ }
153
+ const comparisonStatuses = [
154
+ comparisons.manifest.status,
155
+ comparisons.license.status,
156
+ comparisons.signature.status,
157
+ comparisons.sbom.status,
158
+ ...(expectations.signature.url || actual.remoteArtifacts.signature.fetched
159
+ ? [comparisons.remoteSignature.status]
160
+ : []),
161
+ ...(expectations.sbom.url || actual.remoteArtifacts.sbom.fetched
162
+ ? [comparisons.remoteSbom.status]
163
+ : []),
164
+ ];
165
+ const status = blockers.length
166
+ ? "failed"
167
+ : comparisonStatuses.every((candidate) => candidate === "matched")
168
+ ? "matched"
169
+ : "partial";
170
+ const authority = {
171
+ schemaVersion: PLUGIN_MARKETPLACE_ARTIFACT_READBACK_SCHEMA,
172
+ plugin: {
173
+ name: manifest.metadata.name,
174
+ version: manifest.metadata.version,
175
+ scope: clean(scope, 32),
176
+ },
177
+ catalogAuthority: normalizeCatalogAuthority(source?.catalogAuthority),
178
+ remoteArtifactEvidence,
179
+ expectations,
180
+ actual,
181
+ comparisons,
182
+ blockers,
183
+ };
184
+ return {
185
+ schemaVersion: PLUGIN_MARKETPLACE_ARTIFACT_READBACK_SCHEMA,
186
+ observedAt,
187
+ evidenceDigest: sha256Canonical(authority),
188
+ status,
189
+ ...authority,
190
+ claims: {
191
+ registryPublisherIdentityVerified: false,
192
+ remoteSignatureFetched: actual.remoteArtifacts.signature.fetched === true,
193
+ remoteSignatureBoundToInstalledLock:
194
+ remoteArtifactEvidence.valid === true &&
195
+ actual.signature.verified === true &&
196
+ actual.remoteArtifacts.registryOriginMatches === true &&
197
+ comparisons.remoteSignature.status === "matched" &&
198
+ actual.remoteArtifacts.signature.boundToInstalledLock === true,
199
+ remoteSbomFetched: actual.remoteArtifacts.sbom.fetched === true,
200
+ remoteArtifactEvidenceSelfConsistent:
201
+ remoteArtifactEvidence.valid === true,
202
+ manifestBytesReadFromImmutableInstall: true,
203
+ licenseReadFromInstalledManifest: true,
204
+ signatureCryptographicallyReverified: actual.signature.verified,
205
+ payloadSbomComputedFromImmutableInstall: true,
206
+ externalSbomDigestComparable: comparisons.sbom.comparable,
207
+ remoteSbomDigestVerifiedAtInstallRecorded:
208
+ remoteArtifactEvidence.valid === true &&
209
+ actual.remoteArtifacts.registryOriginMatches === true &&
210
+ comparisons.remoteSbom.status === "matched" &&
211
+ actual.remoteArtifacts.sbom.digestVerifiedAtInstallRecorded === true,
212
+ },
213
+ };
214
+ }
215
+
216
+ export function buildMarketplacePayloadSbom(root) {
217
+ const resolvedRoot = path.resolve(root);
218
+ const files = [];
219
+ let totalBytes = 0;
220
+ const walk = (dir) => {
221
+ const entries = fs
222
+ .readdirSync(dir, { withFileTypes: true })
223
+ .sort((a, b) => stableCompare(a.name, b.name));
224
+ for (const entry of entries) {
225
+ const absolute = path.join(dir, entry.name);
226
+ const relative = path
227
+ .relative(resolvedRoot, absolute)
228
+ .replace(/\\/g, "/");
229
+ if (relative === LOCK_FILENAME || relative === SOURCE_METADATA_FILENAME) {
230
+ continue;
231
+ }
232
+ if (entry.isSymbolicLink()) continue;
233
+ if (entry.isDirectory()) {
234
+ walk(absolute);
235
+ continue;
236
+ }
237
+ if (!entry.isFile()) continue;
238
+ if (files.length >= MAX_MARKETPLACE_READBACK_FILES) {
239
+ throw new Error(
240
+ `marketplace artifact readback exceeds ${MAX_MARKETPLACE_READBACK_FILES} files`,
241
+ );
242
+ }
243
+ const stat = fs.statSync(absolute);
244
+ if (stat.size > MAX_MARKETPLACE_READBACK_FILE_BYTES) {
245
+ throw new Error(
246
+ `marketplace artifact readback file exceeds ${MAX_MARKETPLACE_READBACK_FILE_BYTES} bytes: ${relative}`,
247
+ );
248
+ }
249
+ totalBytes += stat.size;
250
+ if (totalBytes > MAX_MARKETPLACE_READBACK_TOTAL_BYTES) {
251
+ throw new Error(
252
+ `marketplace artifact readback exceeds ${MAX_MARKETPLACE_READBACK_TOTAL_BYTES} total bytes`,
253
+ );
254
+ }
255
+ const bytes = fs.readFileSync(absolute);
256
+ files.push({
257
+ path: relative,
258
+ bytes: bytes.length,
259
+ sha256: sha256(bytes),
260
+ });
261
+ }
262
+ };
263
+ walk(resolvedRoot);
264
+ files.sort((a, b) => stableCompare(a.path, b.path));
265
+ return {
266
+ schemaVersion: PLUGIN_MARKETPLACE_PAYLOAD_SBOM_SCHEMA,
267
+ digest: sha256(
268
+ files
269
+ .map((file) => `${file.path}\0${file.sha256}\0${file.bytes}\n`)
270
+ .join(""),
271
+ ),
272
+ fileCount: files.length,
273
+ totalBytes,
274
+ files,
275
+ exclusions: [LOCK_FILENAME, SOURCE_METADATA_FILENAME],
276
+ };
277
+ }
278
+
279
+ function normalizeExpectations(value) {
280
+ const raw = value && typeof value === "object" ? value : {};
281
+ return {
282
+ manifest: {
283
+ status: expectationStatus(raw.manifest?.status),
284
+ sha256: cleanDigest(raw.manifest?.sha256),
285
+ },
286
+ signature: {
287
+ status: expectationStatus(raw.signature?.status),
288
+ algorithm: clean(raw.signature?.algorithm, 64),
289
+ publicKeySha256: cleanDigest(raw.signature?.publicKeySha256),
290
+ documentSha256: cleanDigest(raw.signature?.documentSha256),
291
+ publicKeyDocumentSha256: cleanDigest(
292
+ raw.signature?.publicKeyDocumentSha256,
293
+ ),
294
+ url: cleanUrl(raw.signature?.url),
295
+ publicKeyUrl: cleanUrl(raw.signature?.publicKeyUrl),
296
+ },
297
+ sbom: {
298
+ status: expectationStatus(raw.sbom?.status),
299
+ format: clean(raw.sbom?.format, 128),
300
+ sha256: cleanDigest(raw.sbom?.payloadSha256 ?? raw.sbom?.sha256),
301
+ payloadSha256: cleanDigest(raw.sbom?.payloadSha256 ?? raw.sbom?.sha256),
302
+ documentSha256: cleanDigest(raw.sbom?.documentSha256),
303
+ url: cleanUrl(raw.sbom?.url),
304
+ },
305
+ license: {
306
+ status: expectationStatus(raw.license?.status),
307
+ expression: clean(raw.license?.expression, 256),
308
+ },
309
+ };
310
+ }
311
+
312
+ function validateRemoteArtifactEvidence(value) {
313
+ if (!value || typeof value !== "object" || Array.isArray(value)) {
314
+ return {
315
+ present: false,
316
+ valid: false,
317
+ evidenceDigest: null,
318
+ authority: null,
319
+ };
320
+ }
321
+ const rawAuthority = {
322
+ schemaVersion: value.schemaVersion,
323
+ status: value.status,
324
+ registryOrigin: value.registryOrigin,
325
+ signature: value.signature ?? null,
326
+ sbom: value.sbom ?? null,
327
+ claims: value.claims ?? null,
328
+ };
329
+ const evidenceDigest = cleanDigest(value.evidenceDigest);
330
+ const authority = normalizeRemoteArtifactAuthority(rawAuthority);
331
+ const valid =
332
+ authority != null &&
333
+ canonicalJson(authority) === canonicalJson(rawAuthority) &&
334
+ evidenceDigest != null &&
335
+ evidenceDigest === sha256Canonical(rawAuthority);
336
+ return {
337
+ present: true,
338
+ valid,
339
+ evidenceDigest,
340
+ authority: valid ? authority : null,
341
+ };
342
+ }
343
+
344
+ function normalizeRemoteArtifactAuthority(value) {
345
+ if (
346
+ value.schemaVersion !==
347
+ "cc-plugin-marketplace-remote-artifact-evidence/v1" ||
348
+ value.status !== "verified"
349
+ ) {
350
+ return null;
351
+ }
352
+ const registryOrigin = cleanRegistryOrigin(value.registryOrigin);
353
+ const signature = normalizeRemoteSignatureEvidence(value.signature);
354
+ const sbom = normalizeRemoteSbomEvidence(value.sbom);
355
+ if (!registryOrigin || (!signature && !sbom)) return null;
356
+ if (value.signature != null && !signature) return null;
357
+ if (value.sbom != null && !sbom) return null;
358
+ const claims = normalizeRemoteArtifactClaims(value.claims);
359
+ if (!claims) return null;
360
+ if (
361
+ claims.publisherIdentityVerified ||
362
+ claims.manifestSignatureVerified ||
363
+ claims.sbomPayloadCompared ||
364
+ claims.signatureBytesFetched !== Boolean(signature) ||
365
+ claims.publicKeyFingerprintVerified !== Boolean(signature) ||
366
+ claims.sbomDocumentDigestVerified !== Boolean(sbom)
367
+ ) {
368
+ return null;
369
+ }
370
+ return {
371
+ schemaVersion: "cc-plugin-marketplace-remote-artifact-evidence/v1",
372
+ status: "verified",
373
+ registryOrigin,
374
+ signature,
375
+ sbom,
376
+ claims,
377
+ };
378
+ }
379
+
380
+ function normalizeRemoteSignatureEvidence(value) {
381
+ if (value == null) return null;
382
+ if (!value || typeof value !== "object" || Array.isArray(value)) return null;
383
+ const url = cleanEvidenceUrl(value.url);
384
+ const signatureSha256 = exactDigest(value.signatureSha256);
385
+ const bytes = boundedEvidenceBytes(value.bytes, 16 * 1024);
386
+ const publicKey = value.publicKey;
387
+ if (
388
+ value.status !== "fetched" ||
389
+ !url ||
390
+ !signatureSha256 ||
391
+ bytes == null ||
392
+ !publicKey ||
393
+ typeof publicKey !== "object" ||
394
+ Array.isArray(publicKey)
395
+ ) {
396
+ return null;
397
+ }
398
+ const publicKeyUrl = cleanEvidenceUrl(publicKey.url);
399
+ const documentSha256 = exactDigest(publicKey.documentSha256);
400
+ const spkiSha256 = exactDigest(publicKey.spkiSha256);
401
+ const publicKeyBytes = boundedEvidenceBytes(publicKey.bytes, 64 * 1024);
402
+ if (
403
+ !publicKeyUrl ||
404
+ !documentSha256 ||
405
+ !spkiSha256 ||
406
+ publicKeyBytes == null
407
+ ) {
408
+ return null;
409
+ }
410
+ return {
411
+ status: "fetched",
412
+ url,
413
+ signatureSha256,
414
+ bytes,
415
+ fromCache: value.fromCache === true,
416
+ publicKey: {
417
+ url: publicKeyUrl,
418
+ documentSha256,
419
+ spkiSha256,
420
+ bytes: publicKeyBytes,
421
+ fromCache: publicKey.fromCache === true,
422
+ },
423
+ };
424
+ }
425
+
426
+ function normalizeRemoteSbomEvidence(value) {
427
+ if (value == null) return null;
428
+ if (!value || typeof value !== "object" || Array.isArray(value)) return null;
429
+ const url = cleanEvidenceUrl(value.url);
430
+ const format = exactClean(value.format, 128, { optional: true });
431
+ const expectedDocumentSha256 = exactDigest(value.expectedDocumentSha256);
432
+ const documentSha256 = exactDigest(value.documentSha256);
433
+ const bytes = boundedEvidenceBytes(value.bytes, 16 * 1024 * 1024);
434
+ if (
435
+ value.status !== "digest-verified" ||
436
+ !url ||
437
+ format === undefined ||
438
+ !expectedDocumentSha256 ||
439
+ !documentSha256 ||
440
+ expectedDocumentSha256 !== documentSha256 ||
441
+ bytes == null
442
+ ) {
443
+ return null;
444
+ }
445
+ return {
446
+ status: "digest-verified",
447
+ url,
448
+ format,
449
+ expectedDocumentSha256,
450
+ documentSha256,
451
+ bytes,
452
+ fromCache: value.fromCache === true,
453
+ };
454
+ }
455
+
456
+ function normalizeRemoteArtifactClaims(value) {
457
+ if (!value || typeof value !== "object" || Array.isArray(value)) return null;
458
+ return {
459
+ publisherIdentityVerified: value.publisherIdentityVerified === true,
460
+ signatureBytesFetched: value.signatureBytesFetched === true,
461
+ publicKeyFingerprintVerified: value.publicKeyFingerprintVerified === true,
462
+ manifestSignatureVerified: value.manifestSignatureVerified === true,
463
+ sbomDocumentDigestVerified: value.sbomDocumentDigestVerified === true,
464
+ sbomPayloadCompared: value.sbomPayloadCompared === true,
465
+ };
466
+ }
467
+
468
+ function summarizeRemoteArtifacts({
469
+ evidence,
470
+ lock,
471
+ signature,
472
+ expectedRegistryOrigin,
473
+ }) {
474
+ const authority = evidence.valid ? evidence.authority : null;
475
+ let installedSignatureSha256 = null;
476
+ if (signature.verified && lock?.signatureBase64) {
477
+ try {
478
+ installedSignatureSha256 = sha256(
479
+ Buffer.from(lock.signatureBase64, "base64"),
480
+ );
481
+ } catch {
482
+ installedSignatureSha256 = null;
483
+ }
484
+ }
485
+ const remoteSignature = authority?.signature;
486
+ const signatureFetched =
487
+ remoteSignature?.status === "fetched" &&
488
+ authority.claims?.signatureBytesFetched === true &&
489
+ authority.claims?.publicKeyFingerprintVerified === true;
490
+ const signatureDigestMatches =
491
+ signatureFetched &&
492
+ installedSignatureSha256 != null &&
493
+ cleanDigest(remoteSignature.signatureSha256) === installedSignatureSha256;
494
+ const publicKeyMatches =
495
+ signatureFetched &&
496
+ signature.publicKeySha256 != null &&
497
+ cleanDigest(remoteSignature.publicKey?.spkiSha256) ===
498
+ signature.publicKeySha256;
499
+ const publicKeyDocumentMatches =
500
+ signatureFetched &&
501
+ signature.publicKeyDocumentSha256 != null &&
502
+ cleanDigest(remoteSignature.publicKey?.documentSha256) ===
503
+ signature.publicKeyDocumentSha256;
504
+ const remoteSbom = authority?.sbom;
505
+ const sbomFetched =
506
+ remoteSbom?.status === "digest-verified" &&
507
+ authority.claims?.sbomDocumentDigestVerified === true;
508
+ const sbomDigestVerified =
509
+ sbomFetched &&
510
+ cleanDigest(remoteSbom.expectedDocumentSha256) != null &&
511
+ cleanDigest(remoteSbom.expectedDocumentSha256) ===
512
+ cleanDigest(remoteSbom.documentSha256);
513
+ const registryOrigin = cleanRegistryOrigin(authority?.registryOrigin);
514
+ const registryOriginComparable =
515
+ evidence.valid && expectedRegistryOrigin != null;
516
+ const registryOriginMatches =
517
+ registryOriginComparable && registryOrigin === expectedRegistryOrigin;
518
+ return {
519
+ evidencePresent: evidence.present,
520
+ evidenceValid: evidence.valid,
521
+ registryOrigin,
522
+ expectedRegistryOrigin,
523
+ registryOriginComparable,
524
+ registryOriginMatches,
525
+ signature: {
526
+ evidencePresent: evidence.present,
527
+ evidenceValid: evidence.valid,
528
+ fetched: signatureFetched,
529
+ url: cleanUrl(remoteSignature?.url),
530
+ publicKeyUrl: cleanUrl(remoteSignature?.publicKey?.url),
531
+ signatureSha256: cleanDigest(remoteSignature?.signatureSha256),
532
+ installedSignatureSha256,
533
+ publicKeySha256: cleanDigest(remoteSignature?.publicKey?.spkiSha256),
534
+ publicKeyDocumentSha256: cleanDigest(
535
+ remoteSignature?.publicKey?.documentSha256,
536
+ ),
537
+ installedPublicKeyDocumentSha256: signature.publicKeyDocumentSha256,
538
+ signatureDigestMatches,
539
+ publicKeyMatches,
540
+ publicKeyDocumentMatches,
541
+ boundToInstalledLock:
542
+ signatureFetched &&
543
+ signatureDigestMatches &&
544
+ publicKeyMatches &&
545
+ publicKeyDocumentMatches,
546
+ registryOriginComparable,
547
+ registryOriginMatches,
548
+ fromCache:
549
+ remoteSignature?.fromCache === true ||
550
+ remoteSignature?.publicKey?.fromCache === true,
551
+ },
552
+ sbom: {
553
+ evidencePresent: evidence.present,
554
+ evidenceValid: evidence.valid,
555
+ fetched: sbomFetched,
556
+ url: cleanUrl(remoteSbom?.url),
557
+ format: clean(remoteSbom?.format, 128),
558
+ documentSha256: cleanDigest(remoteSbom?.documentSha256),
559
+ expectedDocumentSha256: cleanDigest(remoteSbom?.expectedDocumentSha256),
560
+ digestVerifiedAtInstallRecorded: sbomDigestVerified,
561
+ currentDocumentBytesAvailable: false,
562
+ currentDocumentRehashed: false,
563
+ registryOriginComparable,
564
+ registryOriginMatches,
565
+ fromCache: remoteSbom?.fromCache === true,
566
+ },
567
+ };
568
+ }
569
+
570
+ function compareRemoteSignature(expected, actual) {
571
+ if (!expected.url) {
572
+ return { status: "not-declared", comparable: false, expected, actual };
573
+ }
574
+ if (actual.evidencePresent && !actual.evidenceValid) {
575
+ return { status: "mismatch", comparable: true, expected, actual };
576
+ }
577
+ if (!actual.fetched) {
578
+ return { status: "not-observed", comparable: false, expected, actual };
579
+ }
580
+ if (!actual.registryOriginComparable) {
581
+ return { status: "unbound", comparable: false, expected, actual };
582
+ }
583
+ const documentDigestMatches =
584
+ !expected.documentSha256 ||
585
+ expected.documentSha256 === actual.signatureSha256;
586
+ const publicKeyMatchesExpectation =
587
+ expected.publicKeySha256 != null &&
588
+ expected.publicKeySha256 === actual.publicKeySha256;
589
+ const publicKeyDocumentMatches =
590
+ !expected.publicKeyDocumentSha256 ||
591
+ expected.publicKeyDocumentSha256 === actual.publicKeyDocumentSha256;
592
+ const urlsMatch =
593
+ expected.url === actual.url &&
594
+ expected.publicKeyUrl === actual.publicKeyUrl;
595
+ return {
596
+ status:
597
+ actual.boundToInstalledLock &&
598
+ documentDigestMatches &&
599
+ publicKeyMatchesExpectation &&
600
+ publicKeyDocumentMatches &&
601
+ urlsMatch &&
602
+ actual.registryOriginMatches
603
+ ? "matched"
604
+ : "mismatch",
605
+ comparable: true,
606
+ expected: {
607
+ url: expected.url,
608
+ publicKeyUrl: expected.publicKeyUrl,
609
+ publicKeySha256: expected.publicKeySha256,
610
+ documentSha256: expected.documentSha256,
611
+ publicKeyDocumentSha256: expected.publicKeyDocumentSha256,
612
+ },
613
+ actual,
614
+ };
615
+ }
616
+
617
+ function compareRemoteSbom(expected, actual) {
618
+ if (!expected.url) {
619
+ return { status: "not-declared", comparable: false, expected, actual };
620
+ }
621
+ if (actual.evidencePresent && !actual.evidenceValid) {
622
+ return { status: "mismatch", comparable: true, expected, actual };
623
+ }
624
+ if (!actual.fetched) {
625
+ return { status: "not-observed", comparable: false, expected, actual };
626
+ }
627
+ if (!actual.registryOriginComparable) {
628
+ return { status: "unbound", comparable: false, expected, actual };
629
+ }
630
+ const documentDigestMatches =
631
+ expected.documentSha256 != null &&
632
+ expected.documentSha256 === actual.documentSha256;
633
+ const urlMatches = expected.url === actual.url;
634
+ const formatMatches = expected.format === actual.format;
635
+ return {
636
+ status:
637
+ actual.digestVerifiedAtInstallRecorded &&
638
+ documentDigestMatches &&
639
+ urlMatches &&
640
+ formatMatches &&
641
+ actual.registryOriginMatches
642
+ ? "matched"
643
+ : "mismatch",
644
+ comparable: true,
645
+ expected: {
646
+ url: expected.url,
647
+ format: expected.format,
648
+ documentSha256: expected.documentSha256,
649
+ },
650
+ actual,
651
+ };
652
+ }
653
+
654
+ function normalizeCatalogAuthority(value) {
655
+ if (!value || typeof value !== "object") return null;
656
+ return {
657
+ catalogDigest: cleanDigest(value.catalogDigest),
658
+ candidateId: clean(value.candidateId, 64),
659
+ candidateDigest: cleanDigest(value.candidateDigest),
660
+ selectionDigest: cleanDigest(value.selectionDigest),
661
+ updateImpactDigest: cleanDigest(value.updateImpactDigest),
662
+ };
663
+ }
664
+
665
+ function hashLockedPublicKeyDocument(lock) {
666
+ if (typeof lock?.publicKeyPem !== "string" || !lock.publicKeyPem) return null;
667
+ return sha256(Buffer.from(lock.publicKeyPem, "utf8"));
668
+ }
669
+
670
+ function registryOriginFromSource(source) {
671
+ for (const candidate of [source?.registry, source?.source]) {
672
+ const origin = cleanRegistryOriginFromUrl(candidate);
673
+ if (origin) return origin;
674
+ }
675
+ return null;
676
+ }
677
+
678
+ function cleanRegistryOriginFromUrl(value) {
679
+ const candidate = clean(value, 4096);
680
+ if (!candidate) return null;
681
+ try {
682
+ const parsed = new URL(candidate);
683
+ if (
684
+ !["http:", "https:"].includes(parsed.protocol) ||
685
+ parsed.username ||
686
+ parsed.password
687
+ ) {
688
+ return null;
689
+ }
690
+ return parsed.origin;
691
+ } catch {
692
+ return null;
693
+ }
694
+ }
695
+
696
+ function cleanRegistryOrigin(value) {
697
+ const candidate = clean(value, 4096);
698
+ if (!candidate) return null;
699
+ try {
700
+ const parsed = new URL(candidate);
701
+ if (
702
+ !["http:", "https:"].includes(parsed.protocol) ||
703
+ parsed.username ||
704
+ parsed.password ||
705
+ parsed.search ||
706
+ parsed.hash ||
707
+ parsed.pathname !== "/" ||
708
+ parsed.origin !== candidate
709
+ ) {
710
+ return null;
711
+ }
712
+ return parsed.origin;
713
+ } catch {
714
+ return null;
715
+ }
716
+ }
717
+
718
+ function cleanEvidenceUrl(value) {
719
+ const candidate = clean(value, 4096);
720
+ if (!candidate || candidate !== value) return null;
721
+ try {
722
+ const parsed = new URL(candidate);
723
+ if (
724
+ !["http:", "https:"].includes(parsed.protocol) ||
725
+ parsed.username ||
726
+ parsed.password ||
727
+ parsed.search ||
728
+ parsed.hash ||
729
+ parsed.toString() !== candidate
730
+ ) {
731
+ return null;
732
+ }
733
+ return candidate;
734
+ } catch {
735
+ return null;
736
+ }
737
+ }
738
+
739
+ function exactDigest(value) {
740
+ return typeof value === "string" && SHA256_RE.test(value) ? value : null;
741
+ }
742
+
743
+ function boundedEvidenceBytes(value, maximum) {
744
+ return Number.isSafeInteger(value) && value >= 0 && value <= maximum
745
+ ? value
746
+ : null;
747
+ }
748
+
749
+ function exactClean(value, max, { optional = false } = {}) {
750
+ if (value == null) return optional ? null : undefined;
751
+ if (typeof value !== "string") return undefined;
752
+ const normalized = clean(value, max);
753
+ return normalized === value ? normalized : undefined;
754
+ }
755
+
756
+ function compareScalarExpectation(status, expected, actual) {
757
+ if (status !== "declared") {
758
+ return { status: "not-declared", comparable: false, expected, actual };
759
+ }
760
+ if (expected == null) {
761
+ return { status: "unbound", comparable: false, expected, actual };
762
+ }
763
+ return {
764
+ status: expected === actual ? "matched" : "mismatch",
765
+ comparable: true,
766
+ expected,
767
+ actual,
768
+ };
769
+ }
770
+
771
+ function compareSignature(expected, actual) {
772
+ if (expected.status !== "declared") {
773
+ return {
774
+ status: "not-declared",
775
+ comparable: false,
776
+ expected,
777
+ actual,
778
+ };
779
+ }
780
+ if (!actual.verified) {
781
+ return { status: "mismatch", comparable: true, expected, actual };
782
+ }
783
+ if (!expected.publicKeySha256) {
784
+ return { status: "unbound", comparable: false, expected, actual };
785
+ }
786
+ const algorithmMatches =
787
+ !expected.algorithm ||
788
+ expected.algorithm.toLowerCase() === actual.algorithm;
789
+ return {
790
+ status:
791
+ algorithmMatches && expected.publicKeySha256 === actual.publicKeySha256
792
+ ? "matched"
793
+ : "mismatch",
794
+ comparable: true,
795
+ expected,
796
+ actual,
797
+ };
798
+ }
799
+
800
+ function compareSbom(expected, actual) {
801
+ if (expected.status !== "declared") {
802
+ return {
803
+ status: "not-declared",
804
+ comparable: false,
805
+ expected,
806
+ actual: summarizeSbom(actual),
807
+ };
808
+ }
809
+ if (!expected.sha256) {
810
+ return {
811
+ status: "unbound",
812
+ comparable: false,
813
+ expected,
814
+ actual: summarizeSbom(actual),
815
+ };
816
+ }
817
+ if (!COMPARABLE_SBOM_FORMATS.has(expected.format)) {
818
+ return {
819
+ status: "not-comparable",
820
+ comparable: false,
821
+ expected,
822
+ actual: summarizeSbom(actual),
823
+ };
824
+ }
825
+ return {
826
+ status: expected.sha256 === actual.digest ? "matched" : "mismatch",
827
+ comparable: true,
828
+ expected,
829
+ actual: summarizeSbom(actual),
830
+ };
831
+ }
832
+
833
+ function summarizeSbom(value) {
834
+ return {
835
+ schemaVersion: value.schemaVersion,
836
+ digest: value.digest,
837
+ fileCount: value.fileCount,
838
+ totalBytes: value.totalBytes,
839
+ };
840
+ }
841
+
842
+ function expectationStatus(value) {
843
+ return value === "declared" ? "declared" : "missing";
844
+ }
845
+
846
+ function cleanDigest(value) {
847
+ const candidate = clean(value, 64)?.toLowerCase() || null;
848
+ return candidate && SHA256_RE.test(candidate) ? candidate : null;
849
+ }
850
+
851
+ function clean(value, max) {
852
+ if (value == null) return null;
853
+ const candidate = String(value)
854
+ .replace(/\p{Cc}/gu, "")
855
+ .trim()
856
+ .slice(0, max);
857
+ return candidate || null;
858
+ }
859
+
860
+ function cleanUrl(value) {
861
+ const candidate = clean(value, 4096);
862
+ if (!candidate) return null;
863
+ try {
864
+ const parsed = new URL(candidate);
865
+ parsed.username = "";
866
+ parsed.password = "";
867
+ parsed.search = "";
868
+ parsed.hash = "";
869
+ return parsed.toString();
870
+ } catch {
871
+ return null;
872
+ }
873
+ }
874
+
875
+ function issue(code, detail = null) {
876
+ return { code, ...(detail ? { detail: clean(detail, 512) } : {}) };
877
+ }
878
+
879
+ function stableCompare(a, b) {
880
+ return a === b ? 0 : a < b ? -1 : 1;
881
+ }
882
+
883
+ function sha256(value) {
884
+ return crypto.createHash("sha256").update(value).digest("hex");
885
+ }
886
+
887
+ function sha256Canonical(value) {
888
+ return sha256(canonicalJson(value));
889
+ }
890
+
891
+ function canonicalJson(value) {
892
+ if (value == null || typeof value !== "object") return JSON.stringify(value);
893
+ if (Array.isArray(value)) {
894
+ return `[${value.map((item) => canonicalJson(item)).join(",")}]`;
895
+ }
896
+ return `{${Object.keys(value)
897
+ .sort()
898
+ .map((key) => `${JSON.stringify(key)}:${canonicalJson(value[key])}`)
899
+ .join(",")}}`;
900
+ }