chainlesschain 0.166.17 → 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 (210) hide show
  1. package/README.md +14 -7
  2. package/package.json +1 -1
  3. package/src/assets/web-panel/assets/{AIOps-CVW2n2rh.js → AIOps-CQSBl3hY.js} +1 -1
  4. package/src/assets/web-panel/assets/{ActionButton-DtuM163O.js → ActionButton-IQt-vRLt.js} +1 -1
  5. package/src/assets/web-panel/assets/{Analytics-BXvl_qHu.js → Analytics-DkHC8O7k.js} +3 -3
  6. package/src/assets/web-panel/assets/{AppLayout-VDBTFKOp.js → AppLayout-CSti2P2T.js} +3 -3
  7. package/src/assets/web-panel/assets/{Artifacts-BNz2xCiG.js → Artifacts-C57hxn3v.js} +1 -1
  8. package/src/assets/web-panel/assets/{Audit-DTu9Hn9P.js → Audit-DByLntcf.js} +1 -1
  9. package/src/assets/web-panel/assets/{BackgroundAgents-D2OCPzLe.js → BackgroundAgents-D1Bd2EOa.js} +1 -1
  10. package/src/assets/web-panel/assets/{Backup-C9M-vG7G.js → Backup-VstHzHng.js} +1 -1
  11. package/src/assets/web-panel/assets/{BaseInput-BUkCNiUQ.js → BaseInput-CLhdexIf.js} +1 -1
  12. package/src/assets/web-panel/assets/{Chat-OJ2ajF7l.js → Chat-BefC_JZS.js} +6 -6
  13. package/src/assets/web-panel/assets/{ChatBubbleRenderer-BdZt76Ab.js → ChatBubbleRenderer-DbXMvgmn.js} +1 -1
  14. package/src/assets/web-panel/assets/{Checkbox-C1gXKDsc.js → Checkbox-CEY982vx.js} +1 -1
  15. package/src/assets/web-panel/assets/{Codegen-BXbznLg7.js → Codegen-Dcm_DTvJ.js} +1 -1
  16. package/src/assets/web-panel/assets/{Col-CbCq5N0i.js → Col-C8SSu4KA.js} +1 -1
  17. package/src/assets/web-panel/assets/{Community-V8Ks6Yun.js → Community-BVV9vwRR.js} +1 -1
  18. package/src/assets/web-panel/assets/{Compact-DkeHQTpQ.js → Compact-B7c5Vo18.js} +1 -1
  19. package/src/assets/web-panel/assets/{Compliance-JeLKwn4Q.js → Compliance-Tq9jSZ5a.js} +1 -1
  20. package/src/assets/web-panel/assets/{Cowork-azfetcBz.js → Cowork-BJUjT09Q.js} +3 -3
  21. package/src/assets/web-panel/assets/{Cron-Q691i_Gp.js → Cron-CjV9PEaS.js} +2 -2
  22. package/src/assets/web-panel/assets/{Crosschain-BLjDRbr_.js → Crosschain-Dl7ReL8F.js} +1 -1
  23. package/src/assets/web-panel/assets/{DID-D8ptXplK.js → DID-CG8PjWmM.js} +2 -2
  24. package/src/assets/web-panel/assets/{Dashboard-Ctj2VVZL.js → Dashboard-CDBM6wmF.js} +2 -2
  25. package/src/assets/web-panel/assets/{Dropdown-D8VgkMJ8.js → Dropdown-8_55bXTD.js} +1 -1
  26. package/src/assets/web-panel/assets/{EmailListRenderer-CQPe5S6q.js → EmailListRenderer-D3qOU1Cm.js} +1 -1
  27. package/src/assets/web-panel/assets/{FamilyGuardDashboard-ySA-7SiN.js → FamilyGuardDashboard-Bv5oWMZF.js} +1 -1
  28. package/src/assets/web-panel/assets/{Federation-1PMD-fBf.js → Federation-hpZ1xkKd.js} +1 -1
  29. package/src/assets/web-panel/assets/{FormItemContext-C_jr6U4Q.js → FormItemContext-BN7GT191.js} +1 -1
  30. package/src/assets/web-panel/assets/{GenericCardRenderer-Bu3bKf2B.js → GenericCardRenderer-PCRhfA7M.js} +1 -1
  31. package/src/assets/web-panel/assets/{Git-Dv4Fa_T5.js → Git-CIlP2vy7.js} +2 -2
  32. package/src/assets/web-panel/assets/{Governance-DOEy1n1k.js → Governance-81lgm7YL.js} +1 -1
  33. package/src/assets/web-panel/assets/{Inference-DkvqzWcJ.js → Inference-Cs24xMOV.js} +1 -1
  34. package/src/assets/web-panel/assets/{KnowledgeGraph-DdUJK949.js → KnowledgeGraph-CrJLVCZo.js} +1 -1
  35. package/src/assets/web-panel/assets/{Logs-CRelcdUZ.js → Logs-DLyO8RKT.js} +2 -2
  36. package/src/assets/web-panel/assets/{MarkdownRenderer-0AHbbTA4.js → MarkdownRenderer-DBOqijrP.js} +1 -1
  37. package/src/assets/web-panel/assets/{Marketplace-BuuBKsev.js → Marketplace-Dz7AbQZc.js} +1 -1
  38. package/src/assets/web-panel/assets/{McpTools-DlgpEmA1.js → McpTools-BM0aFiWv.js} +3 -3
  39. package/src/assets/web-panel/assets/{Memory-DTfdqUGy.js → Memory-C9SJzENX.js} +2 -2
  40. package/src/assets/web-panel/assets/{MobileBridge-CXLE5ZFO.js → MobileBridge-BO2HnNRk.js} +1 -1
  41. package/src/assets/web-panel/assets/{MobileProjects-Dz25OB0n.js → MobileProjects-CK_jMz0l.js} +1 -1
  42. package/src/assets/web-panel/assets/{Mtc-kEL0fhgD.js → Mtc-CfhrJsdO.js} +2 -2
  43. package/src/assets/web-panel/assets/{MtcAudit-BE4UvFJU.js → MtcAudit-BE2ELxgI.js} +4 -4
  44. package/src/assets/web-panel/assets/{Multisig-CSApSn8c.js → Multisig-CgR_E3Jd.js} +3 -3
  45. package/src/assets/web-panel/assets/{NLProgramming-D11FpKWx.js → NLProgramming-Cw9445_K.js} +1 -1
  46. package/src/assets/web-panel/assets/{Notes-CDL-D2CS.js → Notes-DZimkTrb.js} +3 -3
  47. package/src/assets/web-panel/assets/{NotificationSettings-C0Q6lO-c.js → NotificationSettings-DxHVh4uT.js} +1 -1
  48. package/src/assets/web-panel/assets/OrderTableRenderer-Iy-pnX56.js +1 -0
  49. package/src/assets/web-panel/assets/{Organization-Dc-maVAq.js → Organization-pPXJtWo4.js} +4 -4
  50. package/src/assets/web-panel/assets/{Overflow-B5mUpSvi.js → Overflow-YamdTJwA.js} +1 -1
  51. package/src/assets/web-panel/assets/{P2P-TjXV53wq.js → P2P-BvqP53x7.js} +2 -2
  52. package/src/assets/web-panel/assets/PdhVaultBrowser-Blz1Xn2n.js +7 -0
  53. package/src/assets/web-panel/assets/{Permissions-DKRzQ2O6.js → Permissions-CgFRQqFJ.js} +4 -4
  54. package/src/assets/web-panel/assets/{PersonalDataHub-Dx2eqft8.js → PersonalDataHub-CFeo3-6O.js} +4 -4
  55. package/src/assets/web-panel/assets/{Pipeline-CnYZMB5p.js → Pipeline-BkaRojEK.js} +1 -1
  56. package/src/assets/web-panel/assets/{Privacy-BonKpPnp.js → Privacy-BNZEx2Qp.js} +1 -1
  57. package/src/assets/web-panel/assets/{ProjectInit-DWO0Du3J.js → ProjectInit-HUBOgEEK.js} +2 -2
  58. package/src/assets/web-panel/assets/{ProjectSettings-C9jqyYRv.js → ProjectSettings-CFMmhROS.js} +2 -2
  59. package/src/assets/web-panel/assets/{Projects-CYpKwXbz.js → Projects-Dc517BXp.js} +1 -1
  60. package/src/assets/web-panel/assets/{Providers-CW-fkwJq.js → Providers-DTLf8bgj.js} +1 -1
  61. package/src/assets/web-panel/assets/{QrScannerModal-Cw0J3Zud.js → QrScannerModal-BBNjPszk.js} +1 -1
  62. package/src/assets/web-panel/assets/{QuickAsk-BH4A4IpR.js → QuickAsk-DCsHvk3G.js} +1 -1
  63. package/src/assets/web-panel/assets/{Recommend-CDwMwJus.js → Recommend-Dpr5YSop.js} +1 -1
  64. package/src/assets/web-panel/assets/{RemoteSession-DKIPwzTf.js → RemoteSession-DmKnvqGg.js} +2 -2
  65. package/src/assets/web-panel/assets/{Reputation-CklDj7qS.js → Reputation-CSRfuurX.js} +1 -1
  66. package/src/assets/web-panel/assets/{Row-DJWZKsH0.js → Row-CDj3pQ_w.js} +1 -1
  67. package/src/assets/web-panel/assets/{RssFeed-BfSLCewo.js → RssFeed-DI0a_kNq.js} +2 -2
  68. package/src/assets/web-panel/assets/{Search-B0IST--x.js → Search-jAy7B2nU.js} +1 -1
  69. package/src/assets/web-panel/assets/{Security-h1sYNWtt.js → Security-CkHTqYhc.js} +4 -4
  70. package/src/assets/web-panel/assets/{Services-Gh0-oxI_.js → Services-CP-0Nzga.js} +2 -2
  71. package/src/assets/web-panel/assets/{Skeleton-CNDV_jOs.js → Skeleton-BkF5-p3n.js} +1 -1
  72. package/src/assets/web-panel/assets/{Skills-ByCLTh1n.js → Skills-BDu-utDE.js} +1 -1
  73. package/src/assets/web-panel/assets/{Sla-BfyrJTJz.js → Sla-CQvq_2Qb.js} +1 -1
  74. package/src/assets/web-panel/assets/{SpeechSettings-BQzo9kNX.js → SpeechSettings-T7Ap512g.js} +1 -1
  75. package/src/assets/web-panel/assets/{SyncSettings-B6fS6z_1.js → SyncSettings-Bt-PLvot.js} +2 -2
  76. package/src/assets/web-panel/assets/{Tasks-C-uKCP-l.js → Tasks-yLEcCogg.js} +1 -1
  77. package/src/assets/web-panel/assets/{Templates-D6oDy7wL.js → Templates-ByN3fmbr.js} +1 -1
  78. package/src/assets/web-panel/assets/{Tenant-D3LT2wNQ.js → Tenant-DBI7hITH.js} +1 -1
  79. package/src/assets/web-panel/assets/{Terminal-BteLcv_O.js → Terminal-B77_DlGX.js} +2 -2
  80. package/src/assets/web-panel/assets/{TimelineRenderer-DHHuxfqH.js → TimelineRenderer-CsLTB_QO.js} +1 -1
  81. package/src/assets/web-panel/assets/{Tokens-BRQeykXw.js → Tokens-BV2N6cR8.js} +1 -1
  82. package/src/assets/web-panel/assets/{Trigger-p2XaFWEu.js → Trigger-CjXWGH3p.js} +1 -1
  83. package/src/assets/web-panel/assets/{Trust-BxadBMft.js → Trust-uqXIq3WC.js} +1 -1
  84. package/src/assets/web-panel/assets/{UkeySign-DRBP7LDk.js → UkeySign-D-6qu4vY.js} +1 -1
  85. package/src/assets/web-panel/assets/{VideoEditing-CZ3NZkh7.js → VideoEditing-CxGmx_2e.js} +1 -1
  86. package/src/assets/web-panel/assets/{Wallet-C3NTBN7G.js → Wallet-DSq7WlAg.js} +4 -4
  87. package/src/assets/web-panel/assets/{WebAuthn-C7LVgMlb.js → WebAuthn-CDBiD57O.js} +2 -2
  88. package/src/assets/web-panel/assets/{WorkflowEditor-0_Iveog7.js → WorkflowEditor-Z8f63TSb.js} +1 -1
  89. package/src/assets/web-panel/assets/{chat-DzzluYo3.js → chat-B-UZFBjy.js} +1 -1
  90. package/src/assets/web-panel/assets/{colors-DSMYXpaj.js → colors-CFk7K-7Q.js} +1 -1
  91. package/src/assets/web-panel/assets/{compact-item-W76u1VSV.js → compact-item-X0IVMvgi.js} +1 -1
  92. package/src/assets/web-panel/assets/{createContext-otsESjb5.js → createContext-DWg4j2n6.js} +1 -1
  93. package/src/assets/web-panel/assets/devWarning-B2IW1J_J.js +1 -0
  94. package/src/assets/web-panel/assets/{hasIn-3sNLrvug.js → hasIn-CkVCwNNF.js} +1 -1
  95. package/src/assets/web-panel/assets/{index-BdtmEKat.js → index-5OcZHdzc.js} +1 -1
  96. package/src/assets/web-panel/assets/{index-DS9WYDMf.js → index-B273IY-E.js} +1 -1
  97. package/src/assets/web-panel/assets/{index-NaO9AE-F.js → index-B3-B3FWD.js} +1 -1
  98. package/src/assets/web-panel/assets/{index-BcGJtmHM.js → index-BB4KHHcv.js} +1 -1
  99. package/src/assets/web-panel/assets/index-BCp2ir-1.js +1 -0
  100. package/src/assets/web-panel/assets/{index-pVm_iUre.js → index-BG-GaZri.js} +1 -1
  101. package/src/assets/web-panel/assets/{index-BpcQYzP7.js → index-BPBia4b-.js} +1 -1
  102. package/src/assets/web-panel/assets/{index-BXLx9HfA.js → index-BipwEbTA.js} +1 -1
  103. package/src/assets/web-panel/assets/{index-uSCkbpZH.js → index-BoskgPt9.js} +1 -1
  104. package/src/assets/web-panel/assets/{index-DMmdIpLA.js → index-Btqk6zWb.js} +1 -1
  105. package/src/assets/web-panel/assets/index-Bw4iuzRD.js +1 -0
  106. package/src/assets/web-panel/assets/{index-DLDh1OW3.js → index-BzXjYUmd.js} +1 -1
  107. package/src/assets/web-panel/assets/{index-B9X_jqXe.js → index-C1ZAOJRW.js} +1 -1
  108. package/src/assets/web-panel/assets/{index-vBKPcmAP.js → index-CCLwoATF.js} +1 -1
  109. package/src/assets/web-panel/assets/{index-vUmYkqmu.js → index-CDfSD07S.js} +1 -1
  110. package/src/assets/web-panel/assets/{index-D7A5U2IH.js → index-CLlP1E4X.js} +1 -1
  111. package/src/assets/web-panel/assets/{index-ClOFeZ-t.js → index-CMCEXBRh.js} +1 -1
  112. package/src/assets/web-panel/assets/{index-Cg86EzNp.js → index-CN8HZlLH.js} +1 -1
  113. package/src/assets/web-panel/assets/{index-Ddd4mird.js → index-CNKFsBlz.js} +1 -1
  114. package/src/assets/web-panel/assets/{index-CE7tSow-.js → index-CUBkvFPH.js} +1 -1
  115. package/src/assets/web-panel/assets/{index-GlIOE4ja.js → index-Ca0mij1n.js} +1 -1
  116. package/src/assets/web-panel/assets/{index-DdU7IkKX.js → index-CbIAWbma.js} +1 -1
  117. package/src/assets/web-panel/assets/{index-UUHCwfNq.js → index-CbihMThv.js} +1 -1
  118. package/src/assets/web-panel/assets/{index-BRsD1PT2.js → index-CvOa-IFV.js} +1 -1
  119. package/src/assets/web-panel/assets/{index-C2aht9-M.js → index-Cvwu_rA_.js} +1 -1
  120. package/src/assets/web-panel/assets/{index-BJsQio5O.js → index-D-EJNxgR.js} +1 -1
  121. package/src/assets/web-panel/assets/{index-q7ftLqNt.js → index-D8uEL8Uw.js} +1 -1
  122. package/src/assets/web-panel/assets/{index-B1I1AYJs.js → index-D92g4l3n.js} +1 -1
  123. package/src/assets/web-panel/assets/{index-DZKAzEge.js → index-DCqpl_E_.js} +1 -1
  124. package/src/assets/web-panel/assets/{index-CKoFyHYQ.js → index-DLSBks8h.js} +1 -1
  125. package/src/assets/web-panel/assets/{index-BDlDO2px.js → index-Db_5xhrU.js} +1 -1
  126. package/src/assets/web-panel/assets/{index-CENWwjef.js → index-DcDggpWR.js} +1 -1
  127. package/src/assets/web-panel/assets/{index-DH4cl52p.js → index-DeWKPQGs.js} +1 -1
  128. package/src/assets/web-panel/assets/{index-DTA8Iavv.js → index-DhVHoqGL.js} +3 -3
  129. package/src/assets/web-panel/assets/{index-DmpaC2yk.js → index-DyWlOuak.js} +1 -1
  130. package/src/assets/web-panel/assets/{index-BA8mZxd-.js → index-LU54bChK.js} +1 -1
  131. package/src/assets/web-panel/assets/{index-CtFOt5x1.js → index-OjnAwUJo.js} +1 -1
  132. package/src/assets/web-panel/assets/{index-B5LOYdYm.js → index-P83LDnCz.js} +1 -1
  133. package/src/assets/web-panel/assets/{index-MhQQ3h8q.js → index-_-9k3Tna.js} +1 -1
  134. package/src/assets/web-panel/assets/{initDefaultProps-DF6llUXV.js → initDefaultProps-CFNgY8lJ.js} +1 -1
  135. package/src/assets/web-panel/assets/{motion-vtDV8_5K.js → motion-D-5RrDzn.js} +1 -1
  136. package/src/assets/web-panel/assets/{move-B6c0bdy2.js → move-DN-L0Bd-.js} +1 -1
  137. package/src/assets/web-panel/assets/{mtc-parser-DlLYWuBK.js → mtc-parser-6mgLSeCK.js} +1 -1
  138. package/src/assets/web-panel/assets/{omit-BH0zOxLN.js → omit-ClWW8eTP.js} +1 -1
  139. package/src/assets/web-panel/assets/{pickAttrs-D9di8_In.js → pickAttrs-Vystq3R8.js} +1 -1
  140. package/src/assets/web-panel/assets/{placementArrow-BDSngufM.js → placementArrow-D0H9hK4W.js} +1 -1
  141. package/src/assets/web-panel/assets/{responsiveObserve-C_EawMvP.js → responsiveObserve-B3iNRU-a.js} +1 -1
  142. package/src/assets/web-panel/assets/{slide-B_4wShxA.js → slide-CiMXe6am.js} +1 -1
  143. package/src/assets/web-panel/assets/{statusUtils-CbQu6qJ-.js → statusUtils-CGDZJ1uT.js} +1 -1
  144. package/src/assets/web-panel/assets/{styleChecker-CvXJWYXb.js → styleChecker-D2zA6NkY.js} +1 -1
  145. package/src/assets/web-panel/assets/{useFlexGapSupport-jjY1XknO.js → useFlexGapSupport-C1sYU6QE.js} +1 -1
  146. package/src/assets/web-panel/assets/{useFs-CMXBdsK_.js → useFs-BzUHSjg2.js} +1 -1
  147. package/src/assets/web-panel/assets/{usePersonalDataHub-YbAhhPXx.js → usePersonalDataHub-CxB5yZCq.js} +1 -1
  148. package/src/assets/web-panel/assets/{vnode-BFCTKc0H.js → vnode-BfUfMwtc.js} +1 -1
  149. package/src/assets/web-panel/assets/{zoom-CY9aBl5O.js → zoom-CFYmj1TO.js} +1 -1
  150. package/src/assets/web-panel/index.html +1 -1
  151. package/src/command-help-index.json +1 -1
  152. package/src/commands/agent.js +80 -2
  153. package/src/commands/evolution.js +33 -27
  154. package/src/commands/serve.js +11 -2
  155. package/src/data/changelog.json +5 -5
  156. package/src/gateways/ws/session-protocol.js +1 -0
  157. package/src/gateways/ws/ws-agent-handler.js +70 -2
  158. package/src/gateways/ws/ws-server.js +8 -0
  159. package/src/lib/app-server/cli-agent-kernel-adapter.js +4 -0
  160. package/src/lib/app-server/graph-runtime.js +115 -8
  161. package/src/lib/app-server/server.js +21 -2
  162. package/src/lib/app-server/websocket-transport.js +2 -0
  163. package/src/lib/evolution/agent-evolution-ingress.js +300 -0
  164. package/src/lib/evolution/agent-evolution-runtime-composition-brand.js +22 -0
  165. package/src/lib/evolution/agent-evolution-runtime-composition.js +369 -0
  166. package/src/lib/evolution/evidence-backed-wiki-maintainer.js +752 -0
  167. package/src/lib/evolution/evolution-artifact-ports.js +48 -0
  168. package/src/lib/evolution/evolution-eval-gate.js +11 -4
  169. package/src/lib/evolution/evolution-evidence-artifact-adapter.js +499 -0
  170. package/src/lib/evolution/evolution-evidence-projector.js +61 -1
  171. package/src/lib/evolution/evolution-file-witness.js +601 -0
  172. package/src/lib/evolution/evolution-ledger-file-backend.js +176 -0
  173. package/src/lib/evolution/evolution-ledger-ports.js +353 -16
  174. package/src/lib/evolution/evolution-ledger.js +1355 -54
  175. package/src/lib/evolution/evolution-run-ledger-adapter.js +409 -0
  176. package/src/lib/evolution/evolution-run-wiki-maintenance-source.js +300 -0
  177. package/src/lib/evolution/scheduled-batch-wiki-maintenance-source.js +368 -0
  178. package/src/lib/evolution/skill-candidate-registry.js +649 -0
  179. package/src/lib/evolution/skill-promotion-controller.js +109 -38
  180. package/src/lib/evolution/skill-promotion-review-ledger-adapter.js +451 -0
  181. package/src/lib/evolution/skill-promotion-review.js +793 -0
  182. package/src/lib/evolution/skill-registry-transition-capability.js +55 -0
  183. package/src/lib/evolution/skill-registry-transition-ledger-adapter.js +1280 -0
  184. package/src/lib/evolution/skill-registry-transition-source.js +372 -0
  185. package/src/lib/evolution/skill-release-registry.js +2191 -11
  186. package/src/lib/evolution/skill-target-matrix-eval.js +133 -10
  187. package/src/lib/evolution/structured-memory-agent-control-plane.js +222 -0
  188. package/src/lib/evolution/structured-memory-authority-ledger-adapter.js +351 -0
  189. package/src/lib/evolution/structured-memory-ledger-adapter.js +220 -0
  190. package/src/lib/evolution/structured-memory-policy-receipt-writer.js +164 -0
  191. package/src/lib/evolution/structured-memory-post-compact-hook.js +87 -0
  192. package/src/lib/evolution/structured-memory-promotion-receipt-writer.js +221 -0
  193. package/src/lib/evolution/structured-memory-semantic-review-pipeline.js +313 -0
  194. package/src/lib/evolution/wiki-maintainer-ledger-adapter.js +200 -0
  195. package/src/lib/evolution/wiki-maintenance-trigger-ledger-adapter.js +687 -0
  196. package/src/lib/evolution-system.js +65 -30
  197. package/src/lib/mcp-lifecycle-authority.js +21 -2
  198. package/src/lib/remote-approval-bridge.js +24 -0
  199. package/src/lib/scheduler-kernel/store.js +62 -14
  200. package/src/repl/agent-repl.js +45 -0
  201. package/src/runtime/agent-runtime.js +114 -1
  202. package/src/runtime/headless-runner.js +19 -0
  203. package/src/runtime/headless-stream.js +43 -0
  204. package/src/runtime/runtime-factory.js +19 -1
  205. package/src/assets/web-panel/assets/OrderTableRenderer-ZRE0OKbj.js +0 -1
  206. package/src/assets/web-panel/assets/PdhVaultBrowser-DvV2btIr.js +0 -7
  207. package/src/assets/web-panel/assets/devWarning-BztaXF84.js +0 -1
  208. package/src/assets/web-panel/assets/index-4jP_hG_4.js +0 -1
  209. package/src/assets/web-panel/assets/index-zxwuAXL5.js +0 -1
  210. package/src/lib/learning/learning-hooks.js +0 -123
@@ -13,6 +13,12 @@ import {
13
13
  verifySkillMutationRequest,
14
14
  } from "./skill-mutation-authority.js";
15
15
  import { captureSkillEvaluatedPromotionProvider } from "./skill-evaluated-promotion.js";
16
+ import { captureStructuredMemoryPromotionReceiptWriter } from "./structured-memory-promotion-receipt-writer.js";
17
+ import {
18
+ consumeRegistryTransitionCapability,
19
+ issueRegistryTransitionCapability,
20
+ } from "./skill-registry-transition-capability.js";
21
+ import { captureSkillPromotionReviewProvider } from "./skill-promotion-review.js";
16
22
 
17
23
  const DIGEST_PATTERN = /^sha256:[a-f0-9]{64}$/u;
18
24
  const EMPTY_ACTIVE_DOMAIN = "chainlesschain.skill-active/empty/v1\0";
@@ -23,16 +29,24 @@ export const EMPTY_SKILL_ACTIVE_DIGEST = `sha256:${crypto
23
29
  .update(EMPTY_ACTIVE_DOMAIN, "utf8")
24
30
  .digest("hex")}`;
25
31
 
26
- const TRANSITION_CAPABILITIES = new WeakMap();
27
32
  const EVALUATED_CONTROL_PLANE_OPTION_KEYS = new Set([
28
33
  "candidateRegistry",
29
34
  "releaseRegistry",
30
35
  "authority",
31
36
  "evaluatedPromotionProvider",
37
+ "memoryPromotionReceiptWriter",
38
+ "promotionReviewProvider",
39
+ ]);
40
+ const EVALUATED_CONTROL_PLANE_REQUIRED_KEYS = new Set([
41
+ "candidateRegistry",
42
+ "releaseRegistry",
43
+ "authority",
44
+ "evaluatedPromotionProvider",
32
45
  ]);
33
46
 
34
47
  export const SKILL_EVALUATED_PROMOTION_CONTROL_PLANE_SCHEMA =
35
48
  "chainlesschain.skill-evaluated-promotion-control-plane/v1";
49
+ const EVALUATED_PROMOTION_CONTROL_PLANES = new WeakSet();
36
50
 
37
51
  export class SkillPromotionControllerError extends Error {
38
52
  constructor(code, message, details = {}) {
@@ -211,40 +225,7 @@ function assertTransitionSubject(request, expectedDigest) {
211
225
  }
212
226
  }
213
227
 
214
- function issueRegistryTransitionCapability(registry, payload) {
215
- const capability = Object.freeze(Object.create(null));
216
- TRANSITION_CAPABILITIES.set(capability, {
217
- registry,
218
- payload: deepFreeze(payload),
219
- status: "issued",
220
- });
221
- return capability;
222
- }
223
-
224
- /**
225
- * Registry-only broker. It is exported solely to break the module boundary;
226
- * callers cannot manufacture a WeakMap-backed capability or replay one.
227
- */
228
- export function consumeRegistryTransitionCapability(capability, registry) {
229
- const state =
230
- capability && typeof capability === "object"
231
- ? TRANSITION_CAPABILITIES.get(capability)
232
- : null;
233
- if (!state || state.registry !== registry) {
234
- throw failure(
235
- "SKILL_PROMOTION_TRANSITION_CAPABILITY_INVALID",
236
- "registry transition capability is forged or bound to another registry",
237
- );
238
- }
239
- if (state.status !== "issued") {
240
- throw failure(
241
- "SKILL_PROMOTION_TRANSITION_CAPABILITY_REPLAYED",
242
- "registry transition capability has already been consumed",
243
- );
244
- }
245
- state.status = "consumed";
246
- return state.payload;
247
- }
228
+ export { consumeRegistryTransitionCapability };
248
229
 
249
230
  export class SkillPromotionController {
250
231
  #tenantId;
@@ -265,12 +246,21 @@ export class SkillPromotionController {
265
246
 
266
247
  #requireEvaluatedPromotion;
267
248
 
249
+ #memoryPromotionReceiptWriter;
250
+
251
+ #promotionReviewProvider;
252
+
253
+ #requireHumanReview;
254
+
268
255
  constructor({
269
256
  candidateRegistry,
270
257
  releaseRegistry,
271
258
  authority,
272
259
  evaluatedPromotionProvider = null,
273
260
  requireEvaluatedPromotion = false,
261
+ memoryPromotionReceiptWriter = null,
262
+ promotionReviewProvider = null,
263
+ requireHumanReview = false,
274
264
  } = {}) {
275
265
  if (!candidateRegistry || typeof candidateRegistry.read !== "function") {
276
266
  throw failure(
@@ -320,6 +310,18 @@ export class SkillPromotionController {
320
310
  "evaluated-only promotion requires a captured evidence provider",
321
311
  );
322
312
  }
313
+ if (typeof requireHumanReview !== "boolean") {
314
+ throw failure(
315
+ "SKILL_PROMOTION_INVALID",
316
+ "requireHumanReview must be boolean",
317
+ );
318
+ }
319
+ if (requireHumanReview && promotionReviewProvider === null) {
320
+ throw failure(
321
+ "SKILL_PROMOTION_REVIEW_REQUIRED",
322
+ "human-reviewed promotion requires a captured review provider",
323
+ );
324
+ }
323
325
 
324
326
  this.#tenantId = candidateRegistry.tenantId;
325
327
  this.#registryIdentity = releaseRegistry;
@@ -334,6 +336,17 @@ export class SkillPromotionController {
334
336
  ? null
335
337
  : captureSkillEvaluatedPromotionProvider(evaluatedPromotionProvider);
336
338
  this.#requireEvaluatedPromotion = requireEvaluatedPromotion;
339
+ this.#memoryPromotionReceiptWriter =
340
+ memoryPromotionReceiptWriter === null
341
+ ? null
342
+ : captureStructuredMemoryPromotionReceiptWriter(
343
+ memoryPromotionReceiptWriter,
344
+ );
345
+ this.#promotionReviewProvider =
346
+ promotionReviewProvider === null
347
+ ? null
348
+ : captureSkillPromotionReviewProvider(promotionReviewProvider);
349
+ this.#requireHumanReview = requireHumanReview;
337
350
 
338
351
  Object.freeze(candidateRegistry);
339
352
  Object.freeze(releaseRegistry);
@@ -469,7 +482,11 @@ export class SkillPromotionController {
469
482
  "direct promotion is disabled by the evaluated-only controller policy",
470
483
  );
471
484
  }
472
- return this.#promote(input);
485
+ const result = await this.#promote(input);
486
+ if (this.#memoryPromotionReceiptWriter === null) return result;
487
+ const memoryAuthorityReceipt =
488
+ await this.#memoryPromotionReceiptWriter.retainPromotion(result);
489
+ return deepFreeze({ ...result, memoryAuthorityReceipt });
473
490
  }
474
491
 
475
492
  async promoteEvaluated(input = {}) {
@@ -494,11 +511,39 @@ export class SkillPromotionController {
494
511
  state,
495
512
  activeContentDigest,
496
513
  });
514
+ let reviewBinding = null;
515
+ if (this.#requireHumanReview) {
516
+ const activeRelease =
517
+ state.activeReleaseDigest === null
518
+ ? null
519
+ : this.#readRelease(state.activeReleaseDigest);
520
+ reviewBinding = await this.#promotionReviewProvider.verify({
521
+ activeRelease,
522
+ authorization: input.authorization,
523
+ candidate,
524
+ matrixBinding,
525
+ state,
526
+ });
527
+ }
497
528
  const result = await this.#promote({
498
529
  authorization: input.authorization,
499
530
  candidateId: input.candidateId,
500
531
  });
501
- return deepFreeze({ ...result, matrixBinding });
532
+ if (this.#memoryPromotionReceiptWriter === null) {
533
+ return deepFreeze({ ...result, matrixBinding, reviewBinding });
534
+ }
535
+ const memoryAuthorityReceipt =
536
+ await this.#memoryPromotionReceiptWriter.retainPromotion(
537
+ result,
538
+ matrixBinding,
539
+ reviewBinding,
540
+ );
541
+ return deepFreeze({
542
+ ...result,
543
+ matrixBinding,
544
+ reviewBinding,
545
+ memoryAuthorityReceipt,
546
+ });
502
547
  }
503
548
 
504
549
  async rollback(input = {}) {
@@ -591,27 +636,53 @@ export function createSkillEvaluatedPromotionControlPlane(options = {}) {
591
636
  options,
592
637
  EVALUATED_CONTROL_PLANE_OPTION_KEYS,
593
638
  "evaluated promotion control plane options",
639
+ { required: EVALUATED_CONTROL_PLANE_REQUIRED_KEYS },
594
640
  );
595
641
  const provider = captureSkillEvaluatedPromotionProvider(
596
642
  options.evaluatedPromotionProvider,
597
643
  );
644
+ const reviewProvider = captureSkillPromotionReviewProvider(
645
+ options.promotionReviewProvider,
646
+ );
598
647
  const controller = new SkillPromotionController({
599
648
  candidateRegistry: options.candidateRegistry,
600
649
  releaseRegistry: options.releaseRegistry,
601
650
  authority: options.authority,
602
651
  evaluatedPromotionProvider: options.evaluatedPromotionProvider,
652
+ memoryPromotionReceiptWriter: options.memoryPromotionReceiptWriter ?? null,
653
+ promotionReviewProvider: options.promotionReviewProvider,
603
654
  requireEvaluatedPromotion: true,
655
+ requireHumanReview: true,
604
656
  });
605
- return Object.freeze({
657
+ const controlPlane = {
606
658
  schema: SKILL_EVALUATED_PROMOTION_CONTROL_PLANE_SCHEMA,
607
659
  tenantId: options.candidateRegistry.tenantId,
608
660
  providerAuthorityId: provider.authorityId,
609
661
  providerRevision: provider.revision,
610
662
  providerHandlerArtifactDigest: provider.handlerArtifactDigest,
663
+ reviewAuthorityId: reviewProvider.authorityId,
664
+ reviewRevision: reviewProvider.revision,
665
+ reviewHandlerArtifactDigest: reviewProvider.handlerArtifactDigest,
611
666
  promoteEvaluated: Object.freeze((input) =>
612
667
  controller.promoteEvaluated(input),
613
668
  ),
614
669
  rollback: Object.freeze((input) => controller.rollback(input)),
670
+ };
671
+ EVALUATED_PROMOTION_CONTROL_PLANES.add(controlPlane);
672
+ return Object.freeze(controlPlane);
673
+ }
674
+
675
+ export function captureSkillEvaluatedPromotionControlPlane(value) {
676
+ if (!value || !EVALUATED_PROMOTION_CONTROL_PLANES.has(value)) {
677
+ throw failure(
678
+ "SKILL_PROMOTION_CONTROL_PLANE_REQUIRED",
679
+ "a branded evaluated promotion control plane is required",
680
+ );
681
+ }
682
+ return Object.freeze({
683
+ schema: value.schema,
684
+ tenantId: value.tenantId,
685
+ promoteEvaluated: value.promoteEvaluated.bind(value),
615
686
  });
616
687
  }
617
688
 
@@ -0,0 +1,451 @@
1
+ import {
2
+ EVOLUTION_DURABLE_ARTIFACT_RECORD_SCHEMA,
3
+ isEvolutionLedgerArtifactResolver,
4
+ } from "./evolution-artifact-ports.js";
5
+ import {
6
+ EVOLUTION_ARTIFACT_RESOLUTION_SCHEMA,
7
+ EVOLUTION_LEDGER_DOMAIN_EVENT_SCHEMA,
8
+ } from "./evolution-ledger.js";
9
+ import {
10
+ SKILL_PROMOTION_REVIEW_DECISION_SCHEMA,
11
+ SKILL_PROMOTION_REVIEW_RESOLUTION_SCHEMA,
12
+ captureSkillPromotionReviewPacket,
13
+ verifySkillPromotionReviewDecision,
14
+ verifySkillPromotionReviewPacketArtifact,
15
+ } from "./skill-promotion-review.js";
16
+
17
+ export const SKILL_PROMOTION_REVIEW_REQUEST_EVENT_TYPE =
18
+ "skill.promotion-review.requested";
19
+ export const SKILL_PROMOTION_REVIEW_DECISION_EVENT_TYPE =
20
+ "skill.promotion-review.decided";
21
+ export const SKILL_PROMOTION_REVIEW_LEDGER_CORRUPT_CODE =
22
+ "CC_SKILL_PROMOTION_REVIEW_LEDGER_CORRUPT";
23
+
24
+ const PACKET_TYPE = "skill-promotion-review-packet";
25
+ const DECISION_TYPE = "skill-promotion-review-decision";
26
+ const DIGEST = /^sha256:[a-f0-9]{64}$/u;
27
+
28
+ function requiredString(value, label) {
29
+ if (typeof value !== "string" || value.trim() === "") {
30
+ throw new TypeError(`${label} is required`);
31
+ }
32
+ return value;
33
+ }
34
+
35
+ function capture(owner, method, label = method) {
36
+ if (typeof owner?.[method] !== "function") {
37
+ throw new TypeError(`${label} port is required`);
38
+ }
39
+ return (...args) => Reflect.apply(owner[method], owner, args);
40
+ }
41
+
42
+ function corrupt(message) {
43
+ const error = new Error(message);
44
+ error.code = SKILL_PROMOTION_REVIEW_LEDGER_CORRUPT_CODE;
45
+ throw error;
46
+ }
47
+
48
+ function canonical(value) {
49
+ if (value === null || typeof value !== "object") return JSON.stringify(value);
50
+ if (Array.isArray(value)) return `[${value.map(canonical).join(",")}]`;
51
+ return `{${Object.keys(value)
52
+ .sort()
53
+ .map((key) => `${JSON.stringify(key)}:${canonical(value[key])}`)
54
+ .join(",")}}`;
55
+ }
56
+
57
+ function normalizeDescriptor(input) {
58
+ if (!Number.isSafeInteger(input?.revision) || input.revision < 1) {
59
+ throw new TypeError("revision must be a positive integer");
60
+ }
61
+ if (!DIGEST.test(input?.handlerArtifactDigest || "")) {
62
+ throw new TypeError("handlerArtifactDigest must be sha256-bound");
63
+ }
64
+ return Object.freeze({
65
+ tenantId: requiredString(input.tenantId, "tenantId"),
66
+ artifactTenantId: requiredString(
67
+ input.artifactTenantId,
68
+ "artifactTenantId",
69
+ ),
70
+ streamId: requiredString(input.streamId, "streamId"),
71
+ audience: requiredString(input.audience, "audience"),
72
+ purpose: requiredString(input.purpose, "purpose"),
73
+ authorityId: requiredString(input.authorityId, "authorityId"),
74
+ revision: input.revision,
75
+ handlerArtifactDigest: input.handlerArtifactDigest,
76
+ });
77
+ }
78
+
79
+ function parseRecord(resolution, descriptor, expectedType) {
80
+ if (
81
+ resolution?.schema !== EVOLUTION_ARTIFACT_RESOLUTION_SCHEMA ||
82
+ resolution.authenticated !== true ||
83
+ resolution.found !== true ||
84
+ !Buffer.isBuffer(resolution.bytes)
85
+ ) {
86
+ corrupt("review artifact resolution is unauthenticated or incomplete");
87
+ }
88
+ let record;
89
+ try {
90
+ record = JSON.parse(resolution.bytes.toString("utf8"));
91
+ } catch {
92
+ corrupt("review artifact is not JSON");
93
+ }
94
+ if (
95
+ record?.schema !== EVOLUTION_DURABLE_ARTIFACT_RECORD_SCHEMA ||
96
+ record.tenantId !== descriptor.artifactTenantId ||
97
+ record.audience !== descriptor.audience ||
98
+ record.purpose !== descriptor.purpose ||
99
+ record.retention !== "ledger" ||
100
+ record.type !== expectedType
101
+ ) {
102
+ corrupt("review artifact durable binding is invalid");
103
+ }
104
+ return record.value;
105
+ }
106
+
107
+ export class SkillPromotionReviewLedgerAdapter {
108
+ constructor({
109
+ descriptor: input,
110
+ artifactPorts,
111
+ ledger,
112
+ ledgerArtifactResolver,
113
+ decisionVerifier,
114
+ now = Date.now,
115
+ } = {}) {
116
+ this.descriptor = normalizeDescriptor(input);
117
+ this._put = capture(artifactPorts, "putCanonical");
118
+ this._read = capture(ledger, "read");
119
+ this._verifyLedger = capture(ledger, "verify");
120
+ this._append = capture(ledger, "appendDomainEvent");
121
+ this._verifyDecisionSignature = capture(
122
+ decisionVerifier,
123
+ "verify",
124
+ "decision verifier",
125
+ );
126
+ if (!isEvolutionLedgerArtifactResolver(ledgerArtifactResolver)) {
127
+ throw new TypeError(
128
+ "a branded EvolutionArtifactPorts ledger resolver is required",
129
+ );
130
+ }
131
+ if (typeof now !== "function")
132
+ throw new TypeError("now must be a function");
133
+ this._resolveArtifact = ledgerArtifactResolver;
134
+ this._now = now;
135
+ Object.freeze(this);
136
+ }
137
+
138
+ _events(type) {
139
+ const events = this._read();
140
+ if (!Array.isArray(events))
141
+ corrupt("EvolutionLedger did not return events");
142
+ return events.filter(
143
+ (event) =>
144
+ event.schema === EVOLUTION_LEDGER_DOMAIN_EVENT_SCHEMA &&
145
+ event.type === type &&
146
+ event.tenantId === this.descriptor.tenantId &&
147
+ event.correlationId === this.descriptor.streamId,
148
+ );
149
+ }
150
+
151
+ _resolveEvent(event, expectedType) {
152
+ const identity = this._verifyLedger();
153
+ const resolution = this._resolveArtifact({
154
+ epoch: identity.epoch,
155
+ ledgerId: identity.ledgerId,
156
+ ref: event.subjectRef,
157
+ tenantId: this.descriptor.artifactTenantId,
158
+ });
159
+ if (
160
+ resolution.ref !== event.subjectRef.ref ||
161
+ resolution.digest !== event.subjectRef.digest
162
+ ) {
163
+ corrupt("review ledger subject was substituted");
164
+ }
165
+ return parseRecord(resolution, this.descriptor, expectedType);
166
+ }
167
+
168
+ _packetByDigest(packetDigest) {
169
+ const expectedEventId = `${SKILL_PROMOTION_REVIEW_REQUEST_EVENT_TYPE}.${packetDigest.slice(7)}`;
170
+ const matches = this._events(
171
+ SKILL_PROMOTION_REVIEW_REQUEST_EVENT_TYPE,
172
+ ).filter((event) => event.eventId === expectedEventId);
173
+ if (matches.length !== 1) corrupt("review packet is missing or ambiguous");
174
+ const packet = verifySkillPromotionReviewPacketArtifact(
175
+ this._resolveEvent(matches[0], PACKET_TYPE),
176
+ );
177
+ if (packet.packetDigest !== packetDigest)
178
+ corrupt("review packet was substituted");
179
+ return { event: matches[0], packet };
180
+ }
181
+
182
+ _decisionEntries() {
183
+ return this._events(SKILL_PROMOTION_REVIEW_DECISION_EVENT_TYPE).map(
184
+ (event) => {
185
+ const decision = this._resolveEvent(event, DECISION_TYPE);
186
+ if (
187
+ decision?.schema !== SKILL_PROMOTION_REVIEW_DECISION_SCHEMA ||
188
+ !DIGEST.test(decision.receiptDigest || "") ||
189
+ event.eventId !==
190
+ `${SKILL_PROMOTION_REVIEW_DECISION_EVENT_TYPE}.${decision.receiptDigest.slice(7)}`
191
+ ) {
192
+ corrupt("review decision artifact is invalid");
193
+ }
194
+ const packetEventId = `${SKILL_PROMOTION_REVIEW_REQUEST_EVENT_TYPE}.${decision.packetDigest.slice(7)}`;
195
+ const packetEvents = this._events(
196
+ SKILL_PROMOTION_REVIEW_REQUEST_EVENT_TYPE,
197
+ ).filter((candidate) => candidate.eventId === packetEventId);
198
+ if (
199
+ packetEvents.length !== 1 ||
200
+ !Array.isArray(event.sourceRefs) ||
201
+ event.sourceRefs.length !== 1 ||
202
+ canonical(event.sourceRefs[0]) !==
203
+ canonical(packetEvents[0].subjectRef)
204
+ ) {
205
+ corrupt("review decision packet lineage is invalid");
206
+ }
207
+ return { event, decision };
208
+ },
209
+ );
210
+ }
211
+
212
+ async submitPacket(input) {
213
+ const packet = captureSkillPromotionReviewPacket(input);
214
+ if (packet.tenantId !== this.descriptor.tenantId) {
215
+ throw new TypeError("review packet belongs to another tenant");
216
+ }
217
+ const eventId = `${SKILL_PROMOTION_REVIEW_REQUEST_EVENT_TYPE}.${packet.packetDigest.slice(7)}`;
218
+ const existing = this._events(
219
+ SKILL_PROMOTION_REVIEW_REQUEST_EVENT_TYPE,
220
+ ).filter((event) => event.eventId === eventId);
221
+ if (existing.length > 1)
222
+ corrupt("review packet has duplicate ledger events");
223
+ if (existing.length === 1) {
224
+ const recovered = verifySkillPromotionReviewPacketArtifact(
225
+ this._resolveEvent(existing[0], PACKET_TYPE),
226
+ );
227
+ if (canonical(recovered) !== canonical(packet)) {
228
+ corrupt("review packet id resolved substituted content");
229
+ }
230
+ return Object.freeze({
231
+ persisted: true,
232
+ recovered: true,
233
+ packetDigest: packet.packetDigest,
234
+ });
235
+ }
236
+ const head = this._verifyLedger();
237
+ const nowMs = Number(this._now());
238
+ if (!Number.isFinite(nowMs)) throw new TypeError("review clock is invalid");
239
+ const published = this._put(PACKET_TYPE, packet, {
240
+ audience: this.descriptor.audience,
241
+ purpose: this.descriptor.purpose,
242
+ retention: "ledger",
243
+ });
244
+ if (
245
+ published?.receipt?.persisted !== true ||
246
+ published.receipt.readbackVerified !== true ||
247
+ published.receipt.integrityVerified !== true ||
248
+ published.receipt.retention !== "ledger"
249
+ ) {
250
+ corrupt("review packet persistence was not durably confirmed");
251
+ }
252
+ const timestamp = new Date(nowMs).toISOString();
253
+ const receipt = this._append(
254
+ {
255
+ artifactTenantId: this.descriptor.artifactTenantId,
256
+ correlationId: this.descriptor.streamId,
257
+ decision: "proposed",
258
+ eventId,
259
+ reason: "skill promotion review requested",
260
+ skillName: packet.skillName,
261
+ sourceRefs: [],
262
+ subjectRef: published.ref,
263
+ tenantId: this.descriptor.tenantId,
264
+ timestamp,
265
+ type: SKILL_PROMOTION_REVIEW_REQUEST_EVENT_TYPE,
266
+ },
267
+ { expectedHeadDigest: head.headDigest, expectedSequence: head.sequence },
268
+ );
269
+ if (receipt?.authenticated !== true || receipt.durable !== true) {
270
+ corrupt("review packet ledger append was not durable");
271
+ }
272
+ return Object.freeze({
273
+ persisted: true,
274
+ packetDigest: packet.packetDigest,
275
+ ledgerReceiptDigest: receipt.receiptDigest,
276
+ });
277
+ }
278
+
279
+ async retainDecision(input) {
280
+ if (!DIGEST.test(input?.packetDigest || "")) {
281
+ throw new TypeError("packetDigest is required");
282
+ }
283
+ const { event: packetEvent, packet } = this._packetByDigest(
284
+ input.packetDigest,
285
+ );
286
+ const nowMs = Number(this._now());
287
+ const decision = verifySkillPromotionReviewDecision(
288
+ input.decision,
289
+ packet,
290
+ nowMs,
291
+ );
292
+ if (
293
+ (await this._verifyDecisionSignature({
294
+ decision,
295
+ packet,
296
+ source: "retain",
297
+ })) !== true
298
+ ) {
299
+ throw new Error("review decision signature verification failed");
300
+ }
301
+ const decisionsForPacket = this._decisionEntries().filter(
302
+ ({ decision: stored }) => stored.packetDigest === packet.packetDigest,
303
+ );
304
+ if (decisionsForPacket.length > 1)
305
+ corrupt("review packet has conflicting decisions");
306
+ if (decisionsForPacket.length === 1) {
307
+ if (canonical(decisionsForPacket[0].decision) !== canonical(decision)) {
308
+ corrupt("review packet already has a different decision");
309
+ }
310
+ return Object.freeze({
311
+ persisted: true,
312
+ recovered: true,
313
+ receiptDigest: decision.receiptDigest,
314
+ });
315
+ }
316
+ const head = this._verifyLedger();
317
+ const published = this._put(DECISION_TYPE, decision, {
318
+ audience: this.descriptor.audience,
319
+ purpose: this.descriptor.purpose,
320
+ retention: "ledger",
321
+ });
322
+ if (
323
+ published?.receipt?.persisted !== true ||
324
+ published.receipt.readbackVerified !== true ||
325
+ published.receipt.integrityVerified !== true ||
326
+ published.receipt.retention !== "ledger"
327
+ ) {
328
+ corrupt("review decision persistence was not durably confirmed");
329
+ }
330
+ const eventId = `${SKILL_PROMOTION_REVIEW_DECISION_EVENT_TYPE}.${decision.receiptDigest.slice(7)}`;
331
+ const receipt = this._append(
332
+ {
333
+ artifactTenantId: this.descriptor.artifactTenantId,
334
+ correlationId: this.descriptor.streamId,
335
+ decision: decision.decision === "approved" ? "accepted" : "rejected",
336
+ eventId,
337
+ reason: decision.reason,
338
+ skillName: packet.skillName,
339
+ sourceRefs: [packetEvent.subjectRef],
340
+ subjectRef: published.ref,
341
+ tenantId: this.descriptor.tenantId,
342
+ timestamp: decision.decidedAt,
343
+ type: SKILL_PROMOTION_REVIEW_DECISION_EVENT_TYPE,
344
+ },
345
+ { expectedHeadDigest: head.headDigest, expectedSequence: head.sequence },
346
+ );
347
+ if (receipt?.authenticated !== true || receipt.durable !== true) {
348
+ corrupt("review decision ledger append was not durable");
349
+ }
350
+ return Object.freeze({
351
+ persisted: true,
352
+ receiptDigest: decision.receiptDigest,
353
+ ledgerReceiptDigest: receipt.receiptDigest,
354
+ });
355
+ }
356
+
357
+ async listReviews() {
358
+ const decisions = this._decisionEntries();
359
+ const output = [];
360
+ for (const event of this._events(
361
+ SKILL_PROMOTION_REVIEW_REQUEST_EVENT_TYPE,
362
+ )) {
363
+ const packet = verifySkillPromotionReviewPacketArtifact(
364
+ this._resolveEvent(event, PACKET_TYPE),
365
+ );
366
+ const matches = decisions.filter(
367
+ ({ decision }) => decision.packetDigest === packet.packetDigest,
368
+ );
369
+ if (matches.length > 1)
370
+ corrupt("review packet has conflicting decisions");
371
+ if (matches.length === 1) {
372
+ const decision = verifySkillPromotionReviewDecision(
373
+ matches[0].decision,
374
+ packet,
375
+ Date.parse(matches[0].decision.decidedAt),
376
+ );
377
+ if (
378
+ (await this._verifyDecisionSignature({
379
+ decision,
380
+ packet,
381
+ source: "list",
382
+ ledgerEventDigest: matches[0].event.eventDigest,
383
+ })) !== true
384
+ ) {
385
+ throw new Error("review decision signature verification failed");
386
+ }
387
+ }
388
+ output.push(
389
+ Object.freeze({
390
+ packet,
391
+ decision: matches[0]?.decision || null,
392
+ status:
393
+ matches.length === 1 &&
394
+ Date.parse(matches[0].decision.expiresAt) <= Number(this._now())
395
+ ? "expired"
396
+ : matches[0]?.decision.decision || "pending",
397
+ }),
398
+ );
399
+ }
400
+ return Object.freeze(output);
401
+ }
402
+
403
+ createDecisionResolver() {
404
+ return Object.freeze({
405
+ resolve: async (request) => {
406
+ if (
407
+ request?.tenantId !== this.descriptor.tenantId ||
408
+ !DIGEST.test(request.receiptDigest || "")
409
+ ) {
410
+ corrupt("review decision request is invalid");
411
+ }
412
+ const matches = this._decisionEntries().filter(
413
+ ({ decision }) => decision.receiptDigest === request.receiptDigest,
414
+ );
415
+ if (matches.length !== 1)
416
+ corrupt("review decision is missing or ambiguous");
417
+ const { decision, event } = matches[0];
418
+ const { packet } = this._packetByDigest(decision.packetDigest);
419
+ verifySkillPromotionReviewDecision(
420
+ decision,
421
+ packet,
422
+ Number(this._now()),
423
+ );
424
+ if (
425
+ (await this._verifyDecisionSignature({
426
+ decision,
427
+ packet,
428
+ source: "resolve",
429
+ ledgerEventDigest: event.eventDigest,
430
+ })) !== true
431
+ ) {
432
+ throw new Error("review decision signature verification failed");
433
+ }
434
+ return Object.freeze({
435
+ schema: SKILL_PROMOTION_REVIEW_RESOLUTION_SCHEMA,
436
+ authorityId: this.descriptor.authorityId,
437
+ handlerArtifactDigest: this.descriptor.handlerArtifactDigest,
438
+ revision: this.descriptor.revision,
439
+ tenantId: this.descriptor.tenantId,
440
+ receiptDigest: decision.receiptDigest,
441
+ decision,
442
+ resolvedAt: new Date(Number(this._now())).toISOString(),
443
+ });
444
+ },
445
+ });
446
+ }
447
+ }
448
+
449
+ export function createSkillPromotionReviewLedgerAdapter(options) {
450
+ return new SkillPromotionReviewLedgerAdapter(options);
451
+ }