chainlesschain 0.166.16 → 0.166.18

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 (223) hide show
  1. package/README.md +17 -10
  2. package/completions/_cc +1 -1
  3. package/completions/cc.bash +1 -1
  4. package/completions/cc.fish +1 -1
  5. package/completions/cc.ps1 +1 -1
  6. package/package.json +2 -2
  7. package/src/assets/web-panel/assets/{AIOps-CTxa2Wbq.js → AIOps-CQSBl3hY.js} +1 -1
  8. package/src/assets/web-panel/assets/{ActionButton-DHrQyYhy.js → ActionButton-IQt-vRLt.js} +1 -1
  9. package/src/assets/web-panel/assets/{Analytics-DQiUqcNl.js → Analytics-DkHC8O7k.js} +3 -3
  10. package/src/assets/web-panel/assets/{AppLayout-_Iz4r6wt.js → AppLayout-CSti2P2T.js} +5 -5
  11. package/src/assets/web-panel/assets/{Artifacts-BNs63YHo.js → Artifacts-C57hxn3v.js} +1 -1
  12. package/src/assets/web-panel/assets/{Audit-BgHIqRLn.js → Audit-DByLntcf.js} +1 -1
  13. package/src/assets/web-panel/assets/{BackgroundAgents-DUQ5axBB.js → BackgroundAgents-D1Bd2EOa.js} +1 -1
  14. package/src/assets/web-panel/assets/{Backup-CJ51E9Vw.js → Backup-VstHzHng.js} +1 -1
  15. package/src/assets/web-panel/assets/{BaseInput-o-5toA_R.js → BaseInput-CLhdexIf.js} +1 -1
  16. package/src/assets/web-panel/assets/{Chat-Cu7a-x1L.js → Chat-BefC_JZS.js} +6 -6
  17. package/src/assets/web-panel/assets/{ChatBubbleRenderer-BUyYkAXr.js → ChatBubbleRenderer-DbXMvgmn.js} +1 -1
  18. package/src/assets/web-panel/assets/{Checkbox-DwdmZ71L.js → Checkbox-CEY982vx.js} +1 -1
  19. package/src/assets/web-panel/assets/{Codegen-BrH2eYiP.js → Codegen-Dcm_DTvJ.js} +1 -1
  20. package/src/assets/web-panel/assets/{Col-BVJQEP2j.js → Col-C8SSu4KA.js} +1 -1
  21. package/src/assets/web-panel/assets/{Community-FGO3vZPq.js → Community-BVV9vwRR.js} +1 -1
  22. package/src/assets/web-panel/assets/{Compact-DYGAFVnT.js → Compact-B7c5Vo18.js} +1 -1
  23. package/src/assets/web-panel/assets/{Compliance-CRvc8Bdt.js → Compliance-Tq9jSZ5a.js} +1 -1
  24. package/src/assets/web-panel/assets/{Cowork-D6Ah2m7A.js → Cowork-BJUjT09Q.js} +3 -3
  25. package/src/assets/web-panel/assets/{Cron-AXbU2ifR.js → Cron-CjV9PEaS.js} +2 -2
  26. package/src/assets/web-panel/assets/{Crosschain-C5DXgEFd.js → Crosschain-Dl7ReL8F.js} +1 -1
  27. package/src/assets/web-panel/assets/{DID-Bxh9QV9v.js → DID-CG8PjWmM.js} +2 -2
  28. package/src/assets/web-panel/assets/{Dashboard-B-ZHEta-.js → Dashboard-CDBM6wmF.js} +2 -2
  29. package/src/assets/web-panel/assets/{Dropdown-66Ik1jqa.js → Dropdown-8_55bXTD.js} +1 -1
  30. package/src/assets/web-panel/assets/{EmailListRenderer-CCWk1BIM.js → EmailListRenderer-D3qOU1Cm.js} +1 -1
  31. package/src/assets/web-panel/assets/{FamilyGuardDashboard--9faIULK.js → FamilyGuardDashboard-Bv5oWMZF.js} +1 -1
  32. package/src/assets/web-panel/assets/{Federation-CejW9CgX.js → Federation-hpZ1xkKd.js} +1 -1
  33. package/src/assets/web-panel/assets/{FormItemContext-acHgcXki.js → FormItemContext-BN7GT191.js} +1 -1
  34. package/src/assets/web-panel/assets/{GenericCardRenderer-D2ghd_K4.js → GenericCardRenderer-PCRhfA7M.js} +1 -1
  35. package/src/assets/web-panel/assets/{Git-BOe1YJrV.js → Git-CIlP2vy7.js} +2 -2
  36. package/src/assets/web-panel/assets/{Governance-BjgzJsof.js → Governance-81lgm7YL.js} +1 -1
  37. package/src/assets/web-panel/assets/{Inference-CXCD5rnP.js → Inference-Cs24xMOV.js} +1 -1
  38. package/src/assets/web-panel/assets/{KnowledgeGraph-CHA8QgJT.js → KnowledgeGraph-CrJLVCZo.js} +1 -1
  39. package/src/assets/web-panel/assets/{Logs-DORPMcL6.js → Logs-DLyO8RKT.js} +2 -2
  40. package/src/assets/web-panel/assets/{MarkdownRenderer-CPPHz01z.js → MarkdownRenderer-DBOqijrP.js} +1 -1
  41. package/src/assets/web-panel/assets/{Marketplace-BCFlo1Yx.js → Marketplace-Dz7AbQZc.js} +1 -1
  42. package/src/assets/web-panel/assets/{McpTools-Dl_uZAKh.js → McpTools-BM0aFiWv.js} +3 -3
  43. package/src/assets/web-panel/assets/{Memory-DupHamwz.js → Memory-C9SJzENX.js} +2 -2
  44. package/src/assets/web-panel/assets/{MobileBridge-BRSMPJxW.js → MobileBridge-BO2HnNRk.js} +1 -1
  45. package/src/assets/web-panel/assets/{MobileProjects-BHpuzsDl.js → MobileProjects-CK_jMz0l.js} +1 -1
  46. package/src/assets/web-panel/assets/{Mtc-Ch0jOQfZ.js → Mtc-CfhrJsdO.js} +2 -2
  47. package/src/assets/web-panel/assets/{MtcAudit-qzwQrDi7.js → MtcAudit-BE2ELxgI.js} +4 -4
  48. package/src/assets/web-panel/assets/{Multisig-B5wHzUcm.js → Multisig-CgR_E3Jd.js} +3 -3
  49. package/src/assets/web-panel/assets/{NLProgramming-Hy9uAiA0.js → NLProgramming-Cw9445_K.js} +1 -1
  50. package/src/assets/web-panel/assets/{Notes-DkuchMC7.js → Notes-DZimkTrb.js} +3 -3
  51. package/src/assets/web-panel/assets/{NotificationSettings-CYZhD7Pi.js → NotificationSettings-DxHVh4uT.js} +1 -1
  52. package/src/assets/web-panel/assets/{OrderTableRenderer-DKOoqajt.js → OrderTableRenderer-Iy-pnX56.js} +1 -1
  53. package/src/assets/web-panel/assets/{Organization-D9AXn6j8.js → Organization-pPXJtWo4.js} +4 -4
  54. package/src/assets/web-panel/assets/{Overflow-Bg15J6hX.js → Overflow-YamdTJwA.js} +1 -1
  55. package/src/assets/web-panel/assets/{P2P--B54HoQn.js → P2P-BvqP53x7.js} +2 -2
  56. package/src/assets/web-panel/assets/{PdhVaultBrowser-Cz9Kk2Nb.js → PdhVaultBrowser-Blz1Xn2n.js} +3 -3
  57. package/src/assets/web-panel/assets/{Permissions-DHx38eS1.js → Permissions-CgFRQqFJ.js} +4 -4
  58. package/src/assets/web-panel/assets/{PersonalDataHub-Dw8i2hAs.js → PersonalDataHub-CFeo3-6O.js} +4 -4
  59. package/src/assets/web-panel/assets/{Pipeline-B2o6Ur2G.js → Pipeline-BkaRojEK.js} +1 -1
  60. package/src/assets/web-panel/assets/{Privacy-8g-h2fnu.js → Privacy-BNZEx2Qp.js} +1 -1
  61. package/src/assets/web-panel/assets/{ProjectInit-DuVTcINx.js → ProjectInit-HUBOgEEK.js} +2 -2
  62. package/src/assets/web-panel/assets/{ProjectSettings-C8njvdZX.js → ProjectSettings-CFMmhROS.js} +2 -2
  63. package/src/assets/web-panel/assets/Projects-Dc517BXp.js +1 -0
  64. package/src/assets/web-panel/assets/{Providers-wFqgod_W.js → Providers-DTLf8bgj.js} +1 -1
  65. package/src/assets/web-panel/assets/{QrScannerModal-9NCXrpmA.js → QrScannerModal-BBNjPszk.js} +1 -1
  66. package/src/assets/web-panel/assets/{QuickAsk-WUTNvFkY.js → QuickAsk-DCsHvk3G.js} +1 -1
  67. package/src/assets/web-panel/assets/{Recommend-xT4nYmju.js → Recommend-Dpr5YSop.js} +1 -1
  68. package/src/assets/web-panel/assets/{RemoteSession-fak1PBGC.js → RemoteSession-DmKnvqGg.js} +2 -2
  69. package/src/assets/web-panel/assets/{Reputation-D1AkQvD-.js → Reputation-CSRfuurX.js} +1 -1
  70. package/src/assets/web-panel/assets/{Row-DVfm6xGt.js → Row-CDj3pQ_w.js} +1 -1
  71. package/src/assets/web-panel/assets/{RssFeed-BDunoLSE.js → RssFeed-DI0a_kNq.js} +2 -2
  72. package/src/assets/web-panel/assets/{Search-OPycRoU3.js → Search-jAy7B2nU.js} +1 -1
  73. package/src/assets/web-panel/assets/{Security-C8-Fm3Dt.js → Security-CkHTqYhc.js} +4 -4
  74. package/src/assets/web-panel/assets/{Services-qM_sMpMl.js → Services-CP-0Nzga.js} +2 -2
  75. package/src/assets/web-panel/assets/{Skeleton-D3HBJjVF.js → Skeleton-BkF5-p3n.js} +1 -1
  76. package/src/assets/web-panel/assets/{Skills-jr67X-tP.js → Skills-BDu-utDE.js} +1 -1
  77. package/src/assets/web-panel/assets/{Sla-CS3ggyCg.js → Sla-CQvq_2Qb.js} +1 -1
  78. package/src/assets/web-panel/assets/{SpeechSettings-CTOCYxOi.js → SpeechSettings-T7Ap512g.js} +1 -1
  79. package/src/assets/web-panel/assets/{SyncSettings-D3s3cLT5.js → SyncSettings-Bt-PLvot.js} +2 -2
  80. package/src/assets/web-panel/assets/{Tasks-CdfoJ25m.js → Tasks-yLEcCogg.js} +1 -1
  81. package/src/assets/web-panel/assets/{Templates-Drb4AI8X.js → Templates-ByN3fmbr.js} +1 -1
  82. package/src/assets/web-panel/assets/{Tenant-DzwR3Qkz.js → Tenant-DBI7hITH.js} +1 -1
  83. package/src/assets/web-panel/assets/{Terminal-D-ESq7xp.js → Terminal-B77_DlGX.js} +2 -2
  84. package/src/assets/web-panel/assets/{TimelineRenderer-BK-AUuws.js → TimelineRenderer-CsLTB_QO.js} +1 -1
  85. package/src/assets/web-panel/assets/{Tokens-xGTouuNp.js → Tokens-BV2N6cR8.js} +1 -1
  86. package/src/assets/web-panel/assets/{Trigger-BvZj-v2H.js → Trigger-CjXWGH3p.js} +1 -1
  87. package/src/assets/web-panel/assets/{Trust-BLC9CGmc.js → Trust-uqXIq3WC.js} +1 -1
  88. package/src/assets/web-panel/assets/{UkeySign-BSuCXpmE.js → UkeySign-D-6qu4vY.js} +1 -1
  89. package/src/assets/web-panel/assets/{VideoEditing-DT3A-_Ql.js → VideoEditing-CxGmx_2e.js} +1 -1
  90. package/src/assets/web-panel/assets/{Wallet-c1OXao85.js → Wallet-DSq7WlAg.js} +4 -4
  91. package/src/assets/web-panel/assets/{WebAuthn-CuA-F7KJ.js → WebAuthn-CDBiD57O.js} +2 -2
  92. package/src/assets/web-panel/assets/{WorkflowEditor-MANZrf82.js → WorkflowEditor-Z8f63TSb.js} +1 -1
  93. package/src/assets/web-panel/assets/{chat-D4Y4Ey0O.js → chat-B-UZFBjy.js} +1 -1
  94. package/src/assets/web-panel/assets/{colors-DDH06hKO.js → colors-CFk7K-7Q.js} +1 -1
  95. package/src/assets/web-panel/assets/{compact-item-Cym756Xj.js → compact-item-X0IVMvgi.js} +1 -1
  96. package/src/assets/web-panel/assets/{createContext-By4Nb1k-.js → createContext-DWg4j2n6.js} +1 -1
  97. package/src/assets/web-panel/assets/devWarning-B2IW1J_J.js +1 -0
  98. package/src/assets/web-panel/assets/{hasIn-Ch7OY258.js → hasIn-CkVCwNNF.js} +1 -1
  99. package/src/assets/web-panel/assets/{index-D9le0-dc.js → index-5OcZHdzc.js} +1 -1
  100. package/src/assets/web-panel/assets/{index-CykROEJA.js → index-B273IY-E.js} +1 -1
  101. package/src/assets/web-panel/assets/{index-DjwgJ5Jd.js → index-B3-B3FWD.js} +1 -1
  102. package/src/assets/web-panel/assets/{index-CjNUTBwe.js → index-BB4KHHcv.js} +1 -1
  103. package/src/assets/web-panel/assets/index-BCp2ir-1.js +1 -0
  104. package/src/assets/web-panel/assets/{index-BFydNU6a.js → index-BG-GaZri.js} +1 -1
  105. package/src/assets/web-panel/assets/{index-mL3nOcH6.js → index-BPBia4b-.js} +1 -1
  106. package/src/assets/web-panel/assets/{index-XGeQMvWK.js → index-BipwEbTA.js} +1 -1
  107. package/src/assets/web-panel/assets/{index-D5sADPGY.js → index-BoskgPt9.js} +1 -1
  108. package/src/assets/web-panel/assets/{index-CV8qXTvb.js → index-Btqk6zWb.js} +1 -1
  109. package/src/assets/web-panel/assets/index-Bw4iuzRD.js +1 -0
  110. package/src/assets/web-panel/assets/{index-CJPks33Y.js → index-BzXjYUmd.js} +1 -1
  111. package/src/assets/web-panel/assets/{index-BBBUn5v7.js → index-C1ZAOJRW.js} +1 -1
  112. package/src/assets/web-panel/assets/{index-DiewptTL.js → index-CCLwoATF.js} +1 -1
  113. package/src/assets/web-panel/assets/{index-OU8DZ9BL.js → index-CDfSD07S.js} +1 -1
  114. package/src/assets/web-panel/assets/{index-DvnywwUj.js → index-CLlP1E4X.js} +1 -1
  115. package/src/assets/web-panel/assets/{index-CT-3FYwd.js → index-CMCEXBRh.js} +1 -1
  116. package/src/assets/web-panel/assets/{index-tZ5BUdad.js → index-CN8HZlLH.js} +1 -1
  117. package/src/assets/web-panel/assets/{index-DNlxihs8.js → index-CNKFsBlz.js} +1 -1
  118. package/src/assets/web-panel/assets/{index-CF3Lhyns.js → index-CUBkvFPH.js} +1 -1
  119. package/src/assets/web-panel/assets/{index-CV1UC4zo.js → index-Ca0mij1n.js} +1 -1
  120. package/src/assets/web-panel/assets/{index-DAkk1M7x.js → index-CbIAWbma.js} +1 -1
  121. package/src/assets/web-panel/assets/{index-DNEJzc0r.js → index-CbihMThv.js} +1 -1
  122. package/src/assets/web-panel/assets/{index-q-s04OKn.js → index-CvOa-IFV.js} +1 -1
  123. package/src/assets/web-panel/assets/{index-BZB9q-Lp.js → index-Cvwu_rA_.js} +1 -1
  124. package/src/assets/web-panel/assets/{index-oBsfUbyH.js → index-D-EJNxgR.js} +1 -1
  125. package/src/assets/web-panel/assets/{index-ZY2Qs_Sn.js → index-D8uEL8Uw.js} +1 -1
  126. package/src/assets/web-panel/assets/{index-DtGValNi.js → index-D92g4l3n.js} +1 -1
  127. package/src/assets/web-panel/assets/{index-CpudmDhf.js → index-DCqpl_E_.js} +1 -1
  128. package/src/assets/web-panel/assets/{index-Bo20BfdS.js → index-DLSBks8h.js} +1 -1
  129. package/src/assets/web-panel/assets/{index-CUSUzaB8.js → index-Db_5xhrU.js} +1 -1
  130. package/src/assets/web-panel/assets/{index-CZn6dmZe.js → index-DcDggpWR.js} +1 -1
  131. package/src/assets/web-panel/assets/{index-CGsvEbbq.js → index-DeWKPQGs.js} +1 -1
  132. package/src/assets/web-panel/assets/{index-jx7kr6mB.js → index-DhVHoqGL.js} +3 -3
  133. package/src/assets/web-panel/assets/{index-BIrRX-ra.js → index-DyWlOuak.js} +1 -1
  134. package/src/assets/web-panel/assets/{index-6CpOV3Pn.js → index-LU54bChK.js} +1 -1
  135. package/src/assets/web-panel/assets/{index-6-2eYBXr.js → index-OjnAwUJo.js} +1 -1
  136. package/src/assets/web-panel/assets/{index-B51otVoZ.js → index-P83LDnCz.js} +1 -1
  137. package/src/assets/web-panel/assets/{index-ArMrnx0v.js → index-_-9k3Tna.js} +1 -1
  138. package/src/assets/web-panel/assets/{initDefaultProps-DGcX9iPP.js → initDefaultProps-CFNgY8lJ.js} +1 -1
  139. package/src/assets/web-panel/assets/{motion-2SeL1D8A.js → motion-D-5RrDzn.js} +1 -1
  140. package/src/assets/web-panel/assets/{move-3cYbQCpS.js → move-DN-L0Bd-.js} +1 -1
  141. package/src/assets/web-panel/assets/{mtc-parser-IDdxfw0z.js → mtc-parser-6mgLSeCK.js} +1 -1
  142. package/src/assets/web-panel/assets/{omit-BNswdKO6.js → omit-ClWW8eTP.js} +1 -1
  143. package/src/assets/web-panel/assets/{pickAttrs-BwCnem2I.js → pickAttrs-Vystq3R8.js} +1 -1
  144. package/src/assets/web-panel/assets/{placementArrow-B9PtZ5rq.js → placementArrow-D0H9hK4W.js} +1 -1
  145. package/src/assets/web-panel/assets/{responsiveObserve-CPB6iSzV.js → responsiveObserve-B3iNRU-a.js} +1 -1
  146. package/src/assets/web-panel/assets/{slide-DZgyblZa.js → slide-CiMXe6am.js} +1 -1
  147. package/src/assets/web-panel/assets/{statusUtils-D82IBdsx.js → statusUtils-CGDZJ1uT.js} +1 -1
  148. package/src/assets/web-panel/assets/{styleChecker-qSHtosM-.js → styleChecker-D2zA6NkY.js} +1 -1
  149. package/src/assets/web-panel/assets/{useFlexGapSupport-CmWO7h0I.js → useFlexGapSupport-C1sYU6QE.js} +1 -1
  150. package/src/assets/web-panel/assets/{useFs-yLrs_ub4.js → useFs-BzUHSjg2.js} +1 -1
  151. package/src/assets/web-panel/assets/{usePersonalDataHub-CJ1VW6rh.js → usePersonalDataHub-CxB5yZCq.js} +1 -1
  152. package/src/assets/web-panel/assets/{vnode-CzY_moQ4.js → vnode-BfUfMwtc.js} +1 -1
  153. package/src/assets/web-panel/assets/{zoom-DAa7rEY5.js → zoom-CFYmj1TO.js} +1 -1
  154. package/src/assets/web-panel/index.html +1 -1
  155. package/src/command-help-index.json +2 -2
  156. package/src/command-manifest.json +2 -2
  157. package/src/commands/agent.js +80 -2
  158. package/src/commands/evolution.js +61 -42
  159. package/src/commands/learning.js +24 -8
  160. package/src/commands/serve.js +11 -2
  161. package/src/data/changelog.json +13 -0
  162. package/src/gateways/ws/session-protocol.js +1 -0
  163. package/src/gateways/ws/ws-agent-handler.js +70 -2
  164. package/src/gateways/ws/ws-server.js +8 -0
  165. package/src/lib/app-server/cli-agent-kernel-adapter.js +4 -0
  166. package/src/lib/app-server/graph-runtime.js +115 -8
  167. package/src/lib/app-server/server.js +21 -2
  168. package/src/lib/app-server/websocket-transport.js +2 -0
  169. package/src/lib/evolution/agent-evolution-ingress.js +300 -0
  170. package/src/lib/evolution/agent-evolution-runtime-composition-brand.js +22 -0
  171. package/src/lib/evolution/agent-evolution-runtime-composition.js +369 -0
  172. package/src/lib/evolution/bounded-skill-improvement-pilot.js +498 -0
  173. package/src/lib/evolution/evidence-backed-wiki-maintainer.js +752 -0
  174. package/src/lib/evolution/evolution-artifact-ports.js +48 -0
  175. package/src/lib/evolution/evolution-eval-gate.js +11 -4
  176. package/src/lib/evolution/evolution-evidence-artifact-adapter.js +499 -0
  177. package/src/lib/evolution/evolution-evidence-projector.js +61 -1
  178. package/src/lib/evolution/evolution-file-witness.js +601 -0
  179. package/src/lib/evolution/evolution-ledger-file-backend.js +176 -0
  180. package/src/lib/evolution/evolution-ledger-ports.js +353 -16
  181. package/src/lib/evolution/evolution-ledger.js +1355 -54
  182. package/src/lib/evolution/evolution-run-ledger-adapter.js +409 -0
  183. package/src/lib/evolution/evolution-run-runtime-adapter.js +5 -0
  184. package/src/lib/evolution/evolution-run-wiki-maintenance-source.js +300 -0
  185. package/src/lib/evolution/scheduled-batch-wiki-maintenance-source.js +368 -0
  186. package/src/lib/evolution/skill-candidate-registry.js +649 -0
  187. package/src/lib/evolution/skill-evaluated-promotion-durability.js +899 -0
  188. package/src/lib/evolution/skill-evaluated-promotion.js +481 -0
  189. package/src/lib/evolution/skill-promotion-controller.js +220 -37
  190. package/src/lib/evolution/skill-promotion-review-ledger-adapter.js +451 -0
  191. package/src/lib/evolution/skill-promotion-review.js +793 -0
  192. package/src/lib/evolution/skill-registry-transition-capability.js +55 -0
  193. package/src/lib/evolution/skill-registry-transition-ledger-adapter.js +1280 -0
  194. package/src/lib/evolution/skill-registry-transition-source.js +372 -0
  195. package/src/lib/evolution/skill-release-registry.js +2191 -11
  196. package/src/lib/evolution/skill-target-matrix-eval.js +133 -10
  197. package/src/lib/evolution/structured-memory-agent-control-plane.js +222 -0
  198. package/src/lib/evolution/structured-memory-authority-ledger-adapter.js +351 -0
  199. package/src/lib/evolution/structured-memory-ledger-adapter.js +220 -0
  200. package/src/lib/evolution/structured-memory-policy-receipt-writer.js +164 -0
  201. package/src/lib/evolution/structured-memory-post-compact-hook.js +87 -0
  202. package/src/lib/evolution/structured-memory-promotion-receipt-writer.js +221 -0
  203. package/src/lib/evolution/structured-memory-semantic-review-pipeline.js +313 -0
  204. package/src/lib/evolution/wiki-informed-skill-proposer.js +343 -0
  205. package/src/lib/evolution/wiki-maintainer-ledger-adapter.js +200 -0
  206. package/src/lib/evolution/wiki-maintenance-trigger-ledger-adapter.js +687 -0
  207. package/src/lib/evolution-system.js +65 -30
  208. package/src/lib/graph-kernel/evolution-run-runtime-adapter.js +5 -0
  209. package/src/lib/hooks-v2-runtime.js +20 -0
  210. package/src/lib/mcp-lifecycle-authority.js +21 -2
  211. package/src/lib/remote-approval-bridge.js +24 -0
  212. package/src/lib/scheduler-kernel/store.js +62 -14
  213. package/src/repl/agent-repl.js +45 -0
  214. package/src/runtime/agent-core.js +68 -0
  215. package/src/runtime/agent-runtime.js +114 -1
  216. package/src/runtime/headless-runner.js +19 -0
  217. package/src/runtime/headless-stream.js +43 -0
  218. package/src/runtime/runtime-factory.js +19 -1
  219. package/src/assets/web-panel/assets/Projects-Ca9BAfu7.js +0 -1
  220. package/src/assets/web-panel/assets/devWarning-KO3pAGeO.js +0 -1
  221. package/src/assets/web-panel/assets/index-Bh4Rgkg2.js +0 -1
  222. package/src/assets/web-panel/assets/index-Dox_coTY.js +0 -1
  223. package/src/lib/learning/learning-hooks.js +0 -123
@@ -0,0 +1,793 @@
1
+ /** Human-review protocol for evaluated Skill promotion. */
2
+
3
+ import { createHash } from "node:crypto";
4
+ import { types as utilTypes } from "node:util";
5
+
6
+ import { verifySkillCandidateDraft } from "./skill-candidate-registry.js";
7
+ import { inspectEvolutionContentInjectionRisks } from "./evolution-evidence-projector.js";
8
+ import { verifySkillRelease } from "./skill-release-registry.js";
9
+ import { SKILL_EVALUATED_PROMOTION_BINDING_SCHEMA } from "./skill-evaluated-promotion.js";
10
+
11
+ export const SKILL_PROMOTION_CAPABILITY_DIFF_SCHEMA =
12
+ "chainlesschain.skill-promotion-capability-diff/v1";
13
+ export const SKILL_PROMOTION_REVIEW_PACKET_SCHEMA =
14
+ "chainlesschain.skill-promotion-review-packet/v1";
15
+ export const SKILL_PROMOTION_REVIEW_DECISION_SCHEMA =
16
+ "chainlesschain.skill-promotion-review-decision/v1";
17
+ export const SKILL_PROMOTION_REVIEW_ENVELOPE_SCHEMA =
18
+ "chainlesschain.skill-promotion-review-envelope/v1";
19
+ export const SKILL_PROMOTION_REVIEW_RESOLUTION_SCHEMA =
20
+ "chainlesschain.skill-promotion-review-resolution/v1";
21
+ export const SKILL_PROMOTION_REVIEW_PROVIDER_SCHEMA =
22
+ "chainlesschain.skill-promotion-review-provider/v1";
23
+ export const SKILL_PROMOTION_REVIEW_BINDING_SCHEMA =
24
+ "chainlesschain.skill-promotion-review-binding/v1";
25
+ export const SKILL_PROMOTION_CONTENT_RISK_SCHEMA =
26
+ "chainlesschain.skill-promotion-content-risk/v1";
27
+
28
+ const DIGEST_PATTERN = /^sha256:[a-f0-9]{64}$/u;
29
+ const SAFE_ID_PATTERN = /^[A-Za-z0-9][A-Za-z0-9._:/-]{0,255}$/u;
30
+ const REVIEWERS_MAX = 16;
31
+ const DECISION_MAX_TTL_MS = 15 * 60 * 1000;
32
+ const EMPTY_SKILL_ACTIVE_DIGEST = `sha256:${createHash("sha256")
33
+ .update("chainlesschain.skill-active/empty/v1\0", "utf8")
34
+ .digest("hex")}`;
35
+ const PROVIDERS = new WeakSet();
36
+ const REVIEW_PACKETS = new WeakSet();
37
+ const PROVIDER_KEYS = new Set([
38
+ "schema",
39
+ "authorityId",
40
+ "handlerArtifactDigest",
41
+ "revision",
42
+ "verify",
43
+ ]);
44
+ const PROVIDER_OPTION_KEYS = new Set([
45
+ "authorityId",
46
+ "decisionResolver",
47
+ "decisionVerifier",
48
+ "handlerArtifactDigest",
49
+ "now",
50
+ "revision",
51
+ "tenantId",
52
+ ]);
53
+ const PROVIDER_INPUT_KEYS = new Set([
54
+ "activeRelease",
55
+ "authorization",
56
+ "candidate",
57
+ "matrixBinding",
58
+ "state",
59
+ ]);
60
+ const RESOLUTION_KEYS = new Set([
61
+ "schema",
62
+ "authorityId",
63
+ "handlerArtifactDigest",
64
+ "revision",
65
+ "tenantId",
66
+ "receiptDigest",
67
+ "decision",
68
+ "resolvedAt",
69
+ ]);
70
+ const DECISION_KEYS = new Set([
71
+ "schema",
72
+ "tenantId",
73
+ "skillName",
74
+ "candidateId",
75
+ "packetDigest",
76
+ "decision",
77
+ "automated",
78
+ "reviewerIds",
79
+ "quorum",
80
+ "reason",
81
+ "decidedAt",
82
+ "expiresAt",
83
+ "receiptDigest",
84
+ "signature",
85
+ "acknowledgedContentRiskDigest",
86
+ ]);
87
+ const PACKET_KEYS = new Set([
88
+ "schema",
89
+ "tenantId",
90
+ "skillName",
91
+ "candidateId",
92
+ "candidateContentDigest",
93
+ "parentContentDigest",
94
+ "baselineReleaseDigest",
95
+ "evidenceSummary",
96
+ "candidateDiff",
97
+ "candidateDiffDigest",
98
+ "capabilityDiff",
99
+ "contentRisk",
100
+ "evaluation",
101
+ "targetRuntimes",
102
+ "expectedActiveRevision",
103
+ "requiredHumanQuorum",
104
+ "packetDigest",
105
+ ]);
106
+
107
+ export class SkillPromotionReviewError extends Error {
108
+ constructor(code, message, details = {}) {
109
+ super(message, details.cause ? { cause: details.cause } : undefined);
110
+ this.name = "SkillPromotionReviewError";
111
+ this.code = code;
112
+ for (const [key, value] of Object.entries(details)) {
113
+ if (key !== "cause") this[key] = value;
114
+ }
115
+ }
116
+ }
117
+
118
+ function failure(code, message, details = {}) {
119
+ return new SkillPromotionReviewError(code, message, details);
120
+ }
121
+
122
+ function assertRecord(value, keys, label) {
123
+ if (
124
+ !value ||
125
+ typeof value !== "object" ||
126
+ Array.isArray(value) ||
127
+ utilTypes.isProxy(value) ||
128
+ ![Object.prototype, null].includes(Object.getPrototypeOf(value))
129
+ ) {
130
+ throw failure(
131
+ "SKILL_PROMOTION_REVIEW_INVALID",
132
+ `${label} must be a plain object`,
133
+ );
134
+ }
135
+ const ownKeys = Reflect.ownKeys(value);
136
+ if (
137
+ ownKeys.length !== keys.size ||
138
+ ownKeys.some((key) => typeof key !== "string" || !keys.has(key))
139
+ ) {
140
+ throw failure(
141
+ "SKILL_PROMOTION_REVIEW_INVALID",
142
+ `${label} must contain exactly the supported fields`,
143
+ );
144
+ }
145
+ for (const key of ownKeys) {
146
+ const descriptor = Object.getOwnPropertyDescriptor(value, key);
147
+ if (!descriptor || !("value" in descriptor) || !descriptor.enumerable) {
148
+ throw failure(
149
+ "SKILL_PROMOTION_REVIEW_INVALID",
150
+ `${label}.${String(key)} must be an enumerable data field`,
151
+ );
152
+ }
153
+ }
154
+ }
155
+
156
+ function canonicalJson(value, seen = new Set()) {
157
+ if (value === null || typeof value === "boolean")
158
+ return JSON.stringify(value);
159
+ if (typeof value === "number") {
160
+ if (!Number.isFinite(value))
161
+ throw failure(
162
+ "SKILL_PROMOTION_REVIEW_INVALID",
163
+ "review value contains a non-finite number",
164
+ );
165
+ return JSON.stringify(value);
166
+ }
167
+ if (typeof value === "string") return JSON.stringify(value);
168
+ if (!value || typeof value !== "object" || seen.has(value)) {
169
+ throw failure(
170
+ "SKILL_PROMOTION_REVIEW_INVALID",
171
+ "review value must be acyclic JSON",
172
+ );
173
+ }
174
+ if (utilTypes.isProxy(value))
175
+ throw failure(
176
+ "SKILL_PROMOTION_REVIEW_INVALID",
177
+ "review value must not be a Proxy",
178
+ );
179
+ seen.add(value);
180
+ let output;
181
+ if (Array.isArray(value)) {
182
+ output = `[${value.map((entry) => canonicalJson(entry, seen)).join(",")}]`;
183
+ } else {
184
+ const prototype = Object.getPrototypeOf(value);
185
+ if (prototype !== Object.prototype && prototype !== null) {
186
+ throw failure(
187
+ "SKILL_PROMOTION_REVIEW_INVALID",
188
+ "review value must use plain objects",
189
+ );
190
+ }
191
+ output = `{${Object.keys(value)
192
+ .sort()
193
+ .map((key) => `${JSON.stringify(key)}:${canonicalJson(value[key], seen)}`)
194
+ .join(",")}}`;
195
+ }
196
+ seen.delete(value);
197
+ return output;
198
+ }
199
+
200
+ function digest(domain, value) {
201
+ return `sha256:${createHash("sha256")
202
+ .update(`${domain}\0${canonicalJson(value)}`, "utf8")
203
+ .digest("hex")}`;
204
+ }
205
+
206
+ function clone(value) {
207
+ return JSON.parse(canonicalJson(value));
208
+ }
209
+
210
+ function deepFreeze(value, seen = new WeakSet()) {
211
+ if (!value || typeof value !== "object" || seen.has(value)) return value;
212
+ seen.add(value);
213
+ for (const child of Object.values(value)) deepFreeze(child, seen);
214
+ return Object.freeze(value);
215
+ }
216
+
217
+ function normalizeDigest(value, label) {
218
+ if (typeof value !== "string" || !DIGEST_PATTERN.test(value)) {
219
+ throw failure(
220
+ "SKILL_PROMOTION_REVIEW_INVALID",
221
+ `${label} must be a lowercase SHA-256 digest`,
222
+ );
223
+ }
224
+ return value;
225
+ }
226
+
227
+ function normalizeId(value, label) {
228
+ if (typeof value !== "string" || !SAFE_ID_PATTERN.test(value)) {
229
+ throw failure("SKILL_PROMOTION_REVIEW_INVALID", `${label} is invalid`);
230
+ }
231
+ return value;
232
+ }
233
+
234
+ function normalizeTimestamp(value, label) {
235
+ if (
236
+ typeof value !== "string" ||
237
+ !Number.isFinite(Date.parse(value)) ||
238
+ new Date(value).toISOString() !== value
239
+ ) {
240
+ throw failure("SKILL_PROMOTION_REVIEW_INVALID", `${label} is invalid`);
241
+ }
242
+ return value;
243
+ }
244
+
245
+ function highRiskCapability(capability) {
246
+ return /(?:^|[.:_-])(?:admin|active|exec|network|process|promote|secret|shell|write)(?:$|[.:_-])/u.test(
247
+ capability,
248
+ );
249
+ }
250
+
251
+ function ownData(value, key, label) {
252
+ const descriptor = Object.getOwnPropertyDescriptor(value, key);
253
+ if (!descriptor || !("value" in descriptor) || !descriptor.enumerable) {
254
+ throw failure(
255
+ "SKILL_PROMOTION_REVIEW_INVALID",
256
+ `${label}.${key} must be an enumerable data field`,
257
+ );
258
+ }
259
+ return descriptor.value;
260
+ }
261
+
262
+ function normalizeReviewers(value) {
263
+ if (
264
+ !Array.isArray(value) ||
265
+ utilTypes.isProxy(value) ||
266
+ Object.getPrototypeOf(value) !== Array.prototype ||
267
+ value.length > REVIEWERS_MAX
268
+ ) {
269
+ throw failure(
270
+ "SKILL_PROMOTION_REVIEW_REJECTED",
271
+ "reviewerIds must be a bounded standard array",
272
+ );
273
+ }
274
+ const keys = Reflect.ownKeys(value);
275
+ if (
276
+ keys.length !== value.length + 1 ||
277
+ keys.some(
278
+ (key) =>
279
+ typeof key !== "string" ||
280
+ (key !== "length" && !/^(?:0|[1-9]\d*)$/u.test(key)),
281
+ )
282
+ ) {
283
+ throw failure(
284
+ "SKILL_PROMOTION_REVIEW_REJECTED",
285
+ "reviewerIds must be a dense data array",
286
+ );
287
+ }
288
+ return Array.from({ length: value.length }, (_, index) =>
289
+ normalizeId(ownData(value, String(index), "reviewerIds"), "reviewerId"),
290
+ );
291
+ }
292
+
293
+ export function buildSkillPromotionCapabilityDiff(
294
+ candidate,
295
+ activeRelease = null,
296
+ ) {
297
+ const verifiedCandidate = verifySkillCandidateDraft(candidate);
298
+ const release =
299
+ activeRelease === null ? null : verifySkillRelease(activeRelease);
300
+ if (
301
+ (release === null && verifiedCandidate.parentDigest !== null) ||
302
+ (release !== null &&
303
+ (release.tenantId !== verifiedCandidate.tenantId ||
304
+ release.skillName !== verifiedCandidate.skillName ||
305
+ release.contentDigest !== verifiedCandidate.parentDigest))
306
+ ) {
307
+ throw failure(
308
+ "SKILL_PROMOTION_REVIEW_PARENT_MISMATCH",
309
+ "review baseline is not the candidate parent",
310
+ );
311
+ }
312
+ const baseline = release?.candidate.requestedCapabilities || [];
313
+ const requested = verifiedCandidate.requestedCapabilities;
314
+ const added = requested.filter((item) => !baseline.includes(item));
315
+ const removed = baseline.filter((item) => !requested.includes(item));
316
+ const retained = requested.filter((item) => baseline.includes(item));
317
+ const highRiskAdded = added.filter(highRiskCapability);
318
+ const core = {
319
+ schema: SKILL_PROMOTION_CAPABILITY_DIFF_SCHEMA,
320
+ tenantId: verifiedCandidate.tenantId,
321
+ skillName: verifiedCandidate.skillName,
322
+ candidateId: verifiedCandidate.candidateId,
323
+ baselineReleaseDigest: release?.releaseDigest || null,
324
+ baseline,
325
+ requested,
326
+ added,
327
+ removed,
328
+ retained,
329
+ highRiskAdded,
330
+ changed: added.length > 0 || removed.length > 0,
331
+ requiredHumanQuorum: added.length > 0 ? 2 : 1,
332
+ };
333
+ return deepFreeze({
334
+ ...core,
335
+ capabilityDiffDigest: digest(
336
+ "chainlesschain.skill-promotion-capability-diff/v1",
337
+ core,
338
+ ),
339
+ });
340
+ }
341
+
342
+ function unifiedReplacementDiff(candidate, release) {
343
+ const before = release?.candidate.content || "";
344
+ const after = candidate.content;
345
+ const beforeLines =
346
+ before === "" ? [] : before.replace(/\n$/u, "").split("\n");
347
+ const afterLines = after.replace(/\n$/u, "").split("\n");
348
+ const lines = [
349
+ `--- active/${release?.contentDigest || "empty"}`,
350
+ `+++ candidate/${candidate.contentDigest}`,
351
+ `@@ -1,${beforeLines.length} +1,${afterLines.length} @@`,
352
+ ...beforeLines.map((line) => `-${line}`),
353
+ ...afterLines.map((line) => `+${line}`),
354
+ ];
355
+ return `${lines.join("\n")}\n`;
356
+ }
357
+
358
+ function normalizeMatrixBinding(value, candidate) {
359
+ const keys = new Set([
360
+ "schema",
361
+ "tenantId",
362
+ "skillName",
363
+ "candidateId",
364
+ "candidateContentDigest",
365
+ "expectedActiveContentDigest",
366
+ "expectedActiveRevision",
367
+ "matrixEvalId",
368
+ "matrixReceiptDigest",
369
+ "decisionCommitmentDigest",
370
+ "expiresAt",
371
+ "receiptResolution",
372
+ ]);
373
+ assertRecord(value, keys, "matrix binding");
374
+ if (
375
+ value.schema !== SKILL_EVALUATED_PROMOTION_BINDING_SCHEMA ||
376
+ value.tenantId !== candidate.tenantId ||
377
+ value.skillName !== candidate.skillName ||
378
+ value.candidateId !== candidate.candidateId ||
379
+ value.candidateContentDigest !== candidate.contentDigest
380
+ ) {
381
+ throw failure(
382
+ "SKILL_PROMOTION_REVIEW_EVAL_MISMATCH",
383
+ "matrix binding is not bound to the candidate",
384
+ );
385
+ }
386
+ normalizeDigest(value.matrixReceiptDigest, "matrix receipt digest");
387
+ normalizeDigest(value.decisionCommitmentDigest, "matrix decision commitment");
388
+ normalizeTimestamp(value.expiresAt, "matrix binding expiresAt");
389
+ return {
390
+ schema: value.schema,
391
+ tenantId: value.tenantId,
392
+ skillName: value.skillName,
393
+ candidateId: value.candidateId,
394
+ candidateContentDigest: value.candidateContentDigest,
395
+ expectedActiveContentDigest: value.expectedActiveContentDigest,
396
+ expectedActiveRevision: value.expectedActiveRevision,
397
+ matrixEvalId: value.matrixEvalId,
398
+ matrixReceiptDigest: value.matrixReceiptDigest,
399
+ decisionCommitmentDigest: value.decisionCommitmentDigest,
400
+ expiresAt: value.expiresAt,
401
+ };
402
+ }
403
+
404
+ export function buildSkillPromotionReviewPacket({
405
+ candidate,
406
+ activeRelease = null,
407
+ matrixBinding,
408
+ state,
409
+ } = {}) {
410
+ const verifiedCandidate = verifySkillCandidateDraft(candidate);
411
+ const release =
412
+ activeRelease === null ? null : verifySkillRelease(activeRelease);
413
+ const capabilityDiff = buildSkillPromotionCapabilityDiff(
414
+ verifiedCandidate,
415
+ release,
416
+ );
417
+ const evaluation = normalizeMatrixBinding(matrixBinding, verifiedCandidate);
418
+ if (
419
+ !state ||
420
+ typeof state !== "object" ||
421
+ Array.isArray(state) ||
422
+ utilTypes.isProxy(state) ||
423
+ ![Object.prototype, null].includes(Object.getPrototypeOf(state))
424
+ ) {
425
+ throw failure(
426
+ "SKILL_PROMOTION_REVIEW_PARENT_MISMATCH",
427
+ "review state must be a non-proxy plain object",
428
+ );
429
+ }
430
+ const stateTenantId = ownData(state, "tenantId", "review state");
431
+ const stateSkillName = ownData(state, "skillName", "review state");
432
+ const stateRevision = ownData(state, "revision", "review state");
433
+ const stateActiveReleaseDigest = ownData(
434
+ state,
435
+ "activeReleaseDigest",
436
+ "review state",
437
+ );
438
+ if (
439
+ stateTenantId !== verifiedCandidate.tenantId ||
440
+ stateSkillName !== verifiedCandidate.skillName ||
441
+ !Number.isSafeInteger(stateRevision) ||
442
+ stateRevision < 0 ||
443
+ stateRevision !== evaluation.expectedActiveRevision ||
444
+ stateActiveReleaseDigest !== (release?.releaseDigest || null) ||
445
+ evaluation.expectedActiveContentDigest !==
446
+ (release?.contentDigest || EMPTY_SKILL_ACTIVE_DIGEST)
447
+ ) {
448
+ throw failure(
449
+ "SKILL_PROMOTION_REVIEW_PARENT_MISMATCH",
450
+ "review state, active release, and evaluation baseline do not match",
451
+ );
452
+ }
453
+ const candidateDiff = unifiedReplacementDiff(verifiedCandidate, release);
454
+ const candidateDiffDigest = digest(
455
+ "chainlesschain.skill-promotion-review-unified-diff/v1",
456
+ candidateDiff,
457
+ );
458
+ const evidenceSummary = verifiedCandidate.sourceEvidenceRefs.map(
459
+ ({ digest: evidenceDigest, ref }) => ({ digest: evidenceDigest, ref }),
460
+ );
461
+ const findingIds = [
462
+ ...inspectEvolutionContentInjectionRisks(verifiedCandidate.content),
463
+ ];
464
+ const contentRiskCore = {
465
+ schema: SKILL_PROMOTION_CONTENT_RISK_SCHEMA,
466
+ findingIds,
467
+ detected: findingIds.length > 0,
468
+ };
469
+ const contentRisk = deepFreeze({
470
+ ...contentRiskCore,
471
+ contentRiskDigest: digest(
472
+ "chainlesschain.skill-promotion-content-risk/v1",
473
+ contentRiskCore,
474
+ ),
475
+ });
476
+ const requiredHumanQuorum = Math.max(
477
+ capabilityDiff.requiredHumanQuorum,
478
+ contentRisk.detected ? 2 : 1,
479
+ );
480
+ const core = {
481
+ schema: SKILL_PROMOTION_REVIEW_PACKET_SCHEMA,
482
+ tenantId: verifiedCandidate.tenantId,
483
+ skillName: verifiedCandidate.skillName,
484
+ candidateId: verifiedCandidate.candidateId,
485
+ candidateContentDigest: verifiedCandidate.contentDigest,
486
+ parentContentDigest: verifiedCandidate.parentDigest,
487
+ baselineReleaseDigest: release?.releaseDigest || null,
488
+ evidenceSummary,
489
+ candidateDiff,
490
+ candidateDiffDigest,
491
+ capabilityDiff,
492
+ contentRisk,
493
+ evaluation,
494
+ targetRuntimes: [...verifiedCandidate.targetRuntimes],
495
+ expectedActiveRevision: stateRevision,
496
+ requiredHumanQuorum,
497
+ };
498
+ const packet = deepFreeze({
499
+ ...core,
500
+ packetDigest: digest(
501
+ "chainlesschain.skill-promotion-review-packet/v1",
502
+ core,
503
+ ),
504
+ });
505
+ REVIEW_PACKETS.add(packet);
506
+ return packet;
507
+ }
508
+
509
+ export function captureSkillPromotionReviewPacket(value) {
510
+ if (!value || !REVIEW_PACKETS.has(value)) {
511
+ throw failure(
512
+ "SKILL_PROMOTION_REVIEW_INVALID",
513
+ "a packet produced by the trusted review builder is required",
514
+ );
515
+ }
516
+ return deepFreeze(clone(value));
517
+ }
518
+
519
+ export function verifySkillPromotionReviewPacketArtifact(value) {
520
+ assertRecord(value, PACKET_KEYS, "review packet artifact");
521
+ const core = { ...value };
522
+ delete core.packetDigest;
523
+ if (
524
+ value.schema !== SKILL_PROMOTION_REVIEW_PACKET_SCHEMA ||
525
+ !SAFE_ID_PATTERN.test(value.tenantId || "") ||
526
+ !SAFE_ID_PATTERN.test(value.skillName || "") ||
527
+ !DIGEST_PATTERN.test(value.candidateId || "") ||
528
+ !DIGEST_PATTERN.test(value.candidateContentDigest || "") ||
529
+ !DIGEST_PATTERN.test(value.candidateDiffDigest || "") ||
530
+ !DIGEST_PATTERN.test(value.capabilityDiff?.capabilityDiffDigest || "") ||
531
+ !DIGEST_PATTERN.test(value.contentRisk?.contentRiskDigest || "") ||
532
+ !DIGEST_PATTERN.test(value.evaluation?.matrixReceiptDigest || "") ||
533
+ !Number.isSafeInteger(value.expectedActiveRevision) ||
534
+ value.expectedActiveRevision < 0 ||
535
+ !Number.isSafeInteger(value.requiredHumanQuorum) ||
536
+ value.requiredHumanQuorum < 1 ||
537
+ value.requiredHumanQuorum > REVIEWERS_MAX ||
538
+ value.packetDigest !==
539
+ digest("chainlesschain.skill-promotion-review-packet/v1", core)
540
+ ) {
541
+ throw failure(
542
+ "SKILL_PROMOTION_REVIEW_INVALID",
543
+ "review packet artifact binding is invalid",
544
+ );
545
+ }
546
+ return deepFreeze(clone(value));
547
+ }
548
+
549
+ export function buildSkillPromotionReviewEnvelope(receiptDigest) {
550
+ return canonicalJson({
551
+ schema: SKILL_PROMOTION_REVIEW_ENVELOPE_SCHEMA,
552
+ receiptDigest: normalizeDigest(receiptDigest, "review receipt digest"),
553
+ });
554
+ }
555
+
556
+ function parseReviewEnvelope(value) {
557
+ if (typeof value !== "string" || value.length > 4096) {
558
+ throw failure(
559
+ "SKILL_PROMOTION_REVIEW_INVALID",
560
+ "policyReceipt must be a review envelope",
561
+ );
562
+ }
563
+ let envelope;
564
+ try {
565
+ envelope = JSON.parse(value);
566
+ } catch (cause) {
567
+ throw failure(
568
+ "SKILL_PROMOTION_REVIEW_INVALID",
569
+ "policyReceipt is not JSON",
570
+ { cause },
571
+ );
572
+ }
573
+ assertRecord(
574
+ envelope,
575
+ new Set(["schema", "receiptDigest"]),
576
+ "review envelope",
577
+ );
578
+ if (
579
+ envelope.schema !== SKILL_PROMOTION_REVIEW_ENVELOPE_SCHEMA ||
580
+ canonicalJson(envelope) !== value
581
+ ) {
582
+ throw failure(
583
+ "SKILL_PROMOTION_REVIEW_INVALID",
584
+ "policyReceipt is not a canonical review envelope",
585
+ );
586
+ }
587
+ normalizeDigest(envelope.receiptDigest, "review receipt digest");
588
+ return envelope;
589
+ }
590
+
591
+ function verifyDecision(decision, packet, nowMs, requireApproval = true) {
592
+ assertRecord(decision, DECISION_KEYS, "review decision");
593
+ const core = { ...decision };
594
+ delete core.receiptDigest;
595
+ delete core.signature;
596
+ const reviewers = normalizeReviewers(decision.reviewerIds);
597
+ const decidedAt = normalizeTimestamp(decision.decidedAt, "review decidedAt");
598
+ const expiresAt = normalizeTimestamp(decision.expiresAt, "review expiresAt");
599
+ if (
600
+ decision.schema !== SKILL_PROMOTION_REVIEW_DECISION_SCHEMA ||
601
+ decision.tenantId !== packet.tenantId ||
602
+ decision.skillName !== packet.skillName ||
603
+ decision.candidateId !== packet.candidateId ||
604
+ decision.packetDigest !== packet.packetDigest ||
605
+ !["approved", "rejected"].includes(decision.decision) ||
606
+ (requireApproval && decision.decision !== "approved") ||
607
+ decision.automated !== false ||
608
+ reviewers.length <
609
+ (decision.decision === "approved" ? packet.requiredHumanQuorum : 1) ||
610
+ new Set(reviewers).size !== reviewers.length ||
611
+ decision.quorum !== reviewers.length ||
612
+ decision.acknowledgedContentRiskDigest !==
613
+ (decision.decision === "approved" && packet.contentRisk.detected
614
+ ? packet.contentRisk.contentRiskDigest
615
+ : null) ||
616
+ typeof decision.reason !== "string" ||
617
+ decision.reason.length < 1 ||
618
+ decision.reason.length > 2048 ||
619
+ typeof decision.signature !== "string" ||
620
+ decision.signature.length < 32 ||
621
+ Date.parse(decidedAt) > nowMs ||
622
+ Date.parse(expiresAt) <= nowMs ||
623
+ Date.parse(expiresAt) - Date.parse(decidedAt) > DECISION_MAX_TTL_MS ||
624
+ decision.receiptDigest !==
625
+ digest("chainlesschain.skill-promotion-review-decision/v1", core)
626
+ ) {
627
+ throw failure(
628
+ "SKILL_PROMOTION_REVIEW_REJECTED",
629
+ "human review decision is invalid, stale, automated, rejected, or below quorum",
630
+ );
631
+ }
632
+ return deepFreeze(clone(decision));
633
+ }
634
+
635
+ export function verifySkillPromotionReviewDecision(
636
+ decision,
637
+ packet,
638
+ nowMs = Date.now(),
639
+ ) {
640
+ const verifiedPacket = verifySkillPromotionReviewPacketArtifact(packet);
641
+ if (!Number.isFinite(nowMs)) {
642
+ throw failure(
643
+ "SKILL_PROMOTION_REVIEW_INVALID",
644
+ "review decision verification clock is invalid",
645
+ );
646
+ }
647
+ return verifyDecision(decision, verifiedPacket, nowMs, false);
648
+ }
649
+
650
+ function capturePort(owner, method, label) {
651
+ if (
652
+ !owner ||
653
+ typeof owner !== "object" ||
654
+ utilTypes.isProxy(owner) ||
655
+ typeof owner[method] !== "function"
656
+ ) {
657
+ throw failure(
658
+ "SKILL_PROMOTION_REVIEW_INVALID",
659
+ `${label} must provide ${method}()`,
660
+ );
661
+ }
662
+ const fn = owner[method];
663
+ return (...args) => Reflect.apply(fn, owner, args);
664
+ }
665
+
666
+ export function createSkillPromotionReviewProvider(options = {}) {
667
+ assertRecord(options, PROVIDER_OPTION_KEYS, "review provider options");
668
+ const tenantId = normalizeId(options.tenantId, "review provider tenantId");
669
+ const authorityId = normalizeId(
670
+ options.authorityId,
671
+ "review provider authorityId",
672
+ );
673
+ const handlerArtifactDigest = normalizeDigest(
674
+ options.handlerArtifactDigest,
675
+ "review provider handler digest",
676
+ );
677
+ const revision = options.revision;
678
+ const clock = options.now;
679
+ if (
680
+ !Number.isSafeInteger(revision) ||
681
+ revision < 1 ||
682
+ typeof clock !== "function"
683
+ ) {
684
+ throw failure(
685
+ "SKILL_PROMOTION_REVIEW_INVALID",
686
+ "review provider revision or clock is invalid",
687
+ );
688
+ }
689
+ const resolve = capturePort(
690
+ options.decisionResolver,
691
+ "resolve",
692
+ "review decision resolver",
693
+ );
694
+ const verify = capturePort(
695
+ options.decisionVerifier,
696
+ "verify",
697
+ "review decision verifier",
698
+ );
699
+ const provider = {
700
+ schema: SKILL_PROMOTION_REVIEW_PROVIDER_SCHEMA,
701
+ authorityId,
702
+ handlerArtifactDigest,
703
+ revision,
704
+ async verify(input = {}) {
705
+ assertRecord(input, PROVIDER_INPUT_KEYS, "review provider input");
706
+ const packet = buildSkillPromotionReviewPacket(input);
707
+ if (packet.tenantId !== tenantId) {
708
+ throw failure(
709
+ "SKILL_PROMOTION_REVIEW_REJECTED",
710
+ "review candidate belongs to another tenant",
711
+ );
712
+ }
713
+ const policyReceipt =
714
+ input.authorization?.request?.receipts?.policyReceipt;
715
+ const envelope = parseReviewEnvelope(policyReceipt);
716
+ const resolution = await resolve(
717
+ deepFreeze({ tenantId, receiptDigest: envelope.receiptDigest }),
718
+ );
719
+ assertRecord(resolution, RESOLUTION_KEYS, "review decision resolution");
720
+ if (
721
+ resolution.schema !== SKILL_PROMOTION_REVIEW_RESOLUTION_SCHEMA ||
722
+ resolution.authorityId !== authorityId ||
723
+ resolution.handlerArtifactDigest !== handlerArtifactDigest ||
724
+ resolution.revision !== revision ||
725
+ resolution.tenantId !== tenantId ||
726
+ resolution.receiptDigest !== envelope.receiptDigest
727
+ ) {
728
+ throw failure(
729
+ "SKILL_PROMOTION_REVIEW_REJECTED",
730
+ "review decision resolution is not authority-bound",
731
+ );
732
+ }
733
+ normalizeTimestamp(resolution.resolvedAt, "review resolvedAt");
734
+ const nowMs = Number(clock());
735
+ if (!Number.isFinite(nowMs)) {
736
+ throw failure(
737
+ "SKILL_PROMOTION_REVIEW_INVALID",
738
+ "review provider clock is invalid",
739
+ );
740
+ }
741
+ const decision = verifyDecision(resolution.decision, packet, nowMs);
742
+ if (decision.receiptDigest !== envelope.receiptDigest) {
743
+ throw failure(
744
+ "SKILL_PROMOTION_REVIEW_REJECTED",
745
+ "policyReceipt does not identify the resolved review decision",
746
+ );
747
+ }
748
+ const authenticated = await verify(deepFreeze({ decision, packet }));
749
+ if (authenticated !== true) {
750
+ throw failure(
751
+ "SKILL_PROMOTION_REVIEW_REJECTED",
752
+ "review decision signature verification failed",
753
+ );
754
+ }
755
+ return deepFreeze({
756
+ schema: SKILL_PROMOTION_REVIEW_BINDING_SCHEMA,
757
+ tenantId,
758
+ skillName: packet.skillName,
759
+ candidateId: packet.candidateId,
760
+ packetDigest: packet.packetDigest,
761
+ capabilityDiffDigest: packet.capabilityDiff.capabilityDiffDigest,
762
+ candidateDiffDigest: packet.candidateDiffDigest,
763
+ contentRiskDigest: packet.contentRisk.contentRiskDigest,
764
+ matrixReceiptDigest: packet.evaluation.matrixReceiptDigest,
765
+ reviewReceiptDigest: decision.receiptDigest,
766
+ reviewerIds: [...decision.reviewerIds],
767
+ quorum: decision.quorum,
768
+ expiresAt: decision.expiresAt,
769
+ });
770
+ },
771
+ };
772
+ PROVIDERS.add(provider);
773
+ return Object.freeze(provider);
774
+ }
775
+
776
+ export function captureSkillPromotionReviewProvider(value) {
777
+ if (!value || !PROVIDERS.has(value)) {
778
+ throw failure(
779
+ "SKILL_PROMOTION_REVIEW_PROVIDER_REQUIRED",
780
+ "a branded human review provider is required",
781
+ );
782
+ }
783
+ assertRecord(value, PROVIDER_KEYS, "review provider");
784
+ return Object.freeze({
785
+ schema: value.schema,
786
+ authorityId: value.authorityId,
787
+ handlerArtifactDigest: value.handlerArtifactDigest,
788
+ revision: value.revision,
789
+ verify: value.verify.bind(value),
790
+ });
791
+ }
792
+
793
+ Object.freeze(SkillPromotionReviewError.prototype);