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,1280 @@
1
+ import { createHash } from "node:crypto";
2
+ import { types as utilTypes } from "node:util";
3
+
4
+ import {
5
+ EVOLUTION_DURABLE_ARTIFACT_RECORD_SCHEMA,
6
+ isEvolutionLedgerArtifactResolver,
7
+ } from "./evolution-artifact-ports.js";
8
+ import {
9
+ EVOLUTION_ARTIFACT_RESOLUTION_SCHEMA,
10
+ EVOLUTION_LEDGER_DOMAIN_EVENT_SCHEMA,
11
+ } from "./evolution-ledger.js";
12
+ import {
13
+ EMPTY_SKILL_ACTIVE_DIGEST,
14
+ captureSkillEvaluatedPromotionControlPlane,
15
+ } from "./skill-promotion-controller.js";
16
+ import {
17
+ SKILL_MUTATION_OPERATIONS,
18
+ SKILL_MUTATION_RECEIPT_KINDS,
19
+ SKILL_MUTATION_TARGET_SCOPES,
20
+ SkillMutationAuthority,
21
+ buildSkillMutationRequest,
22
+ digestSkillMutationTransitionSubject,
23
+ verifySkillMutationRequest,
24
+ } from "./skill-mutation-authority.js";
25
+
26
+ export const SKILL_REGISTRY_TRANSITION_REQUEST_SCHEMA =
27
+ "chainlesschain.skill-registry-transition-request/v1";
28
+ export const SKILL_REGISTRY_TRANSITION_ATTEMPT_SCHEMA =
29
+ "chainlesschain.skill-registry-transition-attempt/v1";
30
+ export const SKILL_REGISTRY_TRANSITION_SETTLEMENT_SCHEMA =
31
+ "chainlesschain.skill-registry-transition-settlement/v1";
32
+ export const SKILL_REGISTRY_TRANSITION_REQUEST_EVENT_TYPE =
33
+ "skill.registry-transition.requested";
34
+ export const SKILL_REGISTRY_TRANSITION_ATTEMPT_EVENT_TYPE =
35
+ "skill.registry-transition.attempted";
36
+ export const SKILL_REGISTRY_TRANSITION_SETTLED_EVENT_TYPE =
37
+ "skill.registry-transition.settled";
38
+ export const SKILL_REGISTRY_TRANSITION_CORRUPT_CODE =
39
+ "CC_SKILL_REGISTRY_TRANSITION_CORRUPT";
40
+ export const SKILL_REGISTRY_TRANSITION_CONFLICT_CODE =
41
+ "CC_SKILL_REGISTRY_TRANSITION_CONFLICT";
42
+
43
+ const REQUEST_TYPE = "skill-registry-transition-request";
44
+ const ATTEMPT_TYPE = "skill-registry-transition-attempt";
45
+ const SETTLEMENT_TYPE = "skill-registry-transition-settlement";
46
+ const DIGEST = /^sha256:[a-f0-9]{64}$/u;
47
+ const SKILL_NAME = /^[a-z][a-z0-9]*(?:-[a-z0-9]+)*$/u;
48
+ const REQUEST_ID = /^skill-transition:[a-f0-9]{64}$/u;
49
+ const ATTEMPT_ID = /^skill-transition-attempt:[a-f0-9]{64}$/u;
50
+ const SOURCE_INPUT_KEYS = new Set([
51
+ "candidateCreatedRef",
52
+ "evalCompletedRef",
53
+ "humanTaskSettledRef",
54
+ ]);
55
+ const SOURCE_RESULT_KEYS = new Set([
56
+ "authenticated",
57
+ "durable",
58
+ "tenantId",
59
+ "candidateId",
60
+ "skillName",
61
+ "candidateCreatedRef",
62
+ "evalCompletedRef",
63
+ "humanTaskSettledRef",
64
+ "matrixContext",
65
+ "receipts",
66
+ "effectiveAt",
67
+ "sourceReceiptDigest",
68
+ ]);
69
+ const MATRIX_CONTEXT_KEYS = new Set([
70
+ "baselineId",
71
+ "matrixAuthorityRoot",
72
+ "matrixEvalId",
73
+ "planDigest",
74
+ ]);
75
+ const RECEIPT_KEYS = new Set(
76
+ SKILL_MUTATION_RECEIPT_KINDS.map((kind) => `${kind}Receipt`),
77
+ );
78
+ const REQUEST_KEYS = new Set([
79
+ "schema",
80
+ "tenantId",
81
+ "streamId",
82
+ "candidateId",
83
+ "skillName",
84
+ ...SOURCE_INPUT_KEYS,
85
+ "matrixContext",
86
+ "receipts",
87
+ "effectiveAt",
88
+ "sourceReceiptDigest",
89
+ "requestId",
90
+ "requestDigest",
91
+ ]);
92
+ const ATTEMPT_KEYS = new Set([
93
+ "schema",
94
+ "tenantId",
95
+ "streamId",
96
+ "requestId",
97
+ "requestDigest",
98
+ "attemptId",
99
+ "attemptDigest",
100
+ "ordinal",
101
+ "candidateId",
102
+ "skillName",
103
+ "matrixContext",
104
+ "mutationRequest",
105
+ "createdAt",
106
+ ]);
107
+ const SETTLEMENT_KEYS = new Set([
108
+ "schema",
109
+ "tenantId",
110
+ "streamId",
111
+ "requestId",
112
+ "requestDigest",
113
+ "attemptId",
114
+ "attemptDigest",
115
+ "candidateId",
116
+ "skillName",
117
+ "activeReleaseDigest",
118
+ "authorityReceiptDigest",
119
+ "mutationRequestDigest",
120
+ "transitionSubjectDigest",
121
+ "transactionId",
122
+ "revision",
123
+ "stateDigest",
124
+ "settledAt",
125
+ "settlementDigest",
126
+ ]);
127
+ const AUTHORIZATION_TTL_MS = 4 * 60_000;
128
+
129
+ function canonical(value) {
130
+ if (value === null || typeof value !== "object") return JSON.stringify(value);
131
+ if (Array.isArray(value)) return `[${value.map(canonical).join(",")}]`;
132
+ return `{${Object.keys(value)
133
+ .sort()
134
+ .map((key) => `${JSON.stringify(key)}:${canonical(value[key])}`)
135
+ .join(",")}}`;
136
+ }
137
+
138
+ function hash(value) {
139
+ return `sha256:${createHash("sha256").update(canonical(value)).digest("hex")}`;
140
+ }
141
+
142
+ function same(left, right) {
143
+ return canonical(left) === canonical(right);
144
+ }
145
+
146
+ function deepFreeze(value, seen = new WeakSet()) {
147
+ if (!value || typeof value !== "object" || seen.has(value)) return value;
148
+ seen.add(value);
149
+ Object.freeze(value);
150
+ for (const child of Object.values(value)) deepFreeze(child, seen);
151
+ return value;
152
+ }
153
+
154
+ function fail(code, message, options) {
155
+ const error = new Error(message, options);
156
+ error.code = code;
157
+ throw error;
158
+ }
159
+
160
+ function assertExactRecord(value, keys, label) {
161
+ if (
162
+ !value ||
163
+ typeof value !== "object" ||
164
+ Array.isArray(value) ||
165
+ utilTypes.isProxy(value) ||
166
+ ![Object.prototype, null].includes(Object.getPrototypeOf(value))
167
+ ) {
168
+ throw new TypeError(`${label} must be a plain object`);
169
+ }
170
+ const ownKeys = Reflect.ownKeys(value);
171
+ if (
172
+ ownKeys.length !== keys.size ||
173
+ ownKeys.some((key) => typeof key !== "string" || !keys.has(key))
174
+ ) {
175
+ throw new TypeError(`${label} must contain exactly the supported fields`);
176
+ }
177
+ for (const key of ownKeys) {
178
+ const descriptor = Object.getOwnPropertyDescriptor(value, key);
179
+ if (!descriptor || !("value" in descriptor) || !descriptor.enumerable) {
180
+ throw new TypeError(`${label}.${String(key)} must be a data field`);
181
+ }
182
+ }
183
+ }
184
+
185
+ function requiredString(value, label, maximum = 256) {
186
+ if (
187
+ typeof value !== "string" ||
188
+ value.trim() === "" ||
189
+ value.trim() !== value ||
190
+ Buffer.byteLength(value, "utf8") > maximum
191
+ ) {
192
+ throw new TypeError(`${label} is invalid`);
193
+ }
194
+ return value;
195
+ }
196
+
197
+ function requiredDigest(value, label) {
198
+ if (typeof value !== "string" || !DIGEST.test(value)) {
199
+ throw new TypeError(`${label} must be sha256-bound`);
200
+ }
201
+ return value;
202
+ }
203
+
204
+ function timestamp(value, label) {
205
+ requiredString(value, label);
206
+ const parsed = new Date(value);
207
+ if (!Number.isFinite(parsed.getTime()) || parsed.toISOString() !== value) {
208
+ throw new TypeError(`${label} must be a canonical timestamp`);
209
+ }
210
+ return value;
211
+ }
212
+
213
+ function capture(owner, method, label = method) {
214
+ if (
215
+ !owner ||
216
+ typeof owner !== "object" ||
217
+ utilTypes.isProxy(owner) ||
218
+ typeof owner[method] !== "function"
219
+ ) {
220
+ throw new TypeError(`${label} port is required`);
221
+ }
222
+ return (...args) => Reflect.apply(owner[method], owner, args);
223
+ }
224
+
225
+ function normalizeDescriptor(input) {
226
+ return Object.freeze({
227
+ tenantId: requiredString(input?.tenantId, "tenantId"),
228
+ artifactTenantId: requiredString(
229
+ input?.artifactTenantId,
230
+ "artifactTenantId",
231
+ ),
232
+ streamId: requiredString(input?.streamId, "streamId"),
233
+ audience: requiredString(input?.audience, "audience"),
234
+ purpose: requiredString(input?.purpose, "purpose"),
235
+ });
236
+ }
237
+
238
+ function normalizeSourceInput(input) {
239
+ assertExactRecord(input, SOURCE_INPUT_KEYS, "transition source input");
240
+ return deepFreeze({
241
+ candidateCreatedRef: requiredString(
242
+ input.candidateCreatedRef,
243
+ "candidateCreatedRef",
244
+ 512,
245
+ ),
246
+ evalCompletedRef: requiredString(
247
+ input.evalCompletedRef,
248
+ "evalCompletedRef",
249
+ 512,
250
+ ),
251
+ humanTaskSettledRef: requiredString(
252
+ input.humanTaskSettledRef,
253
+ "humanTaskSettledRef",
254
+ 512,
255
+ ),
256
+ });
257
+ }
258
+
259
+ function normalizeMatrixContext(value) {
260
+ assertExactRecord(value, MATRIX_CONTEXT_KEYS, "matrixContext");
261
+ return deepFreeze({
262
+ baselineId: requiredString(value.baselineId, "matrixContext.baselineId"),
263
+ matrixAuthorityRoot: requiredDigest(
264
+ value.matrixAuthorityRoot,
265
+ "matrixContext.matrixAuthorityRoot",
266
+ ),
267
+ matrixEvalId: requiredString(
268
+ value.matrixEvalId,
269
+ "matrixContext.matrixEvalId",
270
+ ),
271
+ planDigest: requiredDigest(value.planDigest, "matrixContext.planDigest"),
272
+ });
273
+ }
274
+
275
+ function normalizeReceipts(value) {
276
+ assertExactRecord(value, RECEIPT_KEYS, "transition receipt envelopes");
277
+ return deepFreeze(
278
+ Object.fromEntries(
279
+ SKILL_MUTATION_RECEIPT_KINDS.map((kind) => {
280
+ const key = `${kind}Receipt`;
281
+ return [key, requiredString(value[key], key, 4096)];
282
+ }),
283
+ ),
284
+ );
285
+ }
286
+
287
+ function normalizeVerifiedSource(value, descriptor, expectedInput) {
288
+ assertExactRecord(value, SOURCE_RESULT_KEYS, "transition source resolution");
289
+ if (
290
+ value.authenticated !== true ||
291
+ value.durable !== true ||
292
+ value.tenantId !== descriptor.tenantId ||
293
+ value.candidateCreatedRef !== expectedInput.candidateCreatedRef ||
294
+ value.evalCompletedRef !== expectedInput.evalCompletedRef ||
295
+ value.humanTaskSettledRef !== expectedInput.humanTaskSettledRef ||
296
+ !DIGEST.test(value.candidateId ?? "") ||
297
+ !SKILL_NAME.test(value.skillName ?? "") ||
298
+ !DIGEST.test(value.sourceReceiptDigest ?? "")
299
+ ) {
300
+ fail(
301
+ SKILL_REGISTRY_TRANSITION_CORRUPT_CODE,
302
+ "transition source did not authenticate the exact event chain",
303
+ );
304
+ }
305
+ const core = {
306
+ schema: SKILL_REGISTRY_TRANSITION_REQUEST_SCHEMA,
307
+ tenantId: descriptor.tenantId,
308
+ streamId: descriptor.streamId,
309
+ candidateId: value.candidateId,
310
+ skillName: value.skillName,
311
+ candidateCreatedRef: value.candidateCreatedRef,
312
+ evalCompletedRef: value.evalCompletedRef,
313
+ humanTaskSettledRef: value.humanTaskSettledRef,
314
+ matrixContext: normalizeMatrixContext(value.matrixContext),
315
+ receipts: normalizeReceipts(value.receipts),
316
+ effectiveAt: timestamp(value.effectiveAt, "source effectiveAt"),
317
+ sourceReceiptDigest: value.sourceReceiptDigest,
318
+ };
319
+ if (Buffer.byteLength(canonical(core), "utf8") > 64 * 1024) {
320
+ throw new TypeError("transition request exceeds its durable size limit");
321
+ }
322
+ const requestDigest = hash(core);
323
+ return deepFreeze({
324
+ ...core,
325
+ requestId: `skill-transition:${requestDigest.slice(7)}`,
326
+ requestDigest,
327
+ });
328
+ }
329
+
330
+ function verifyRequest(value, descriptor) {
331
+ assertExactRecord(value, REQUEST_KEYS, "stored transition request");
332
+ if (
333
+ value.schema !== SKILL_REGISTRY_TRANSITION_REQUEST_SCHEMA ||
334
+ value.tenantId !== descriptor.tenantId ||
335
+ value.streamId !== descriptor.streamId ||
336
+ !REQUEST_ID.test(value.requestId ?? "") ||
337
+ !DIGEST.test(value.requestDigest ?? "")
338
+ ) {
339
+ fail(
340
+ SKILL_REGISTRY_TRANSITION_CORRUPT_CODE,
341
+ "stored transition request identity is invalid",
342
+ );
343
+ }
344
+ requiredDigest(value.candidateId, "request candidateId");
345
+ if (!SKILL_NAME.test(value.skillName ?? "")) {
346
+ fail(
347
+ SKILL_REGISTRY_TRANSITION_CORRUPT_CODE,
348
+ "stored transition request skillName is invalid",
349
+ );
350
+ }
351
+ normalizeSourceInput({
352
+ candidateCreatedRef: value.candidateCreatedRef,
353
+ evalCompletedRef: value.evalCompletedRef,
354
+ humanTaskSettledRef: value.humanTaskSettledRef,
355
+ });
356
+ normalizeMatrixContext(value.matrixContext);
357
+ normalizeReceipts(value.receipts);
358
+ timestamp(value.effectiveAt, "request effectiveAt");
359
+ requiredDigest(value.sourceReceiptDigest, "request sourceReceiptDigest");
360
+ const core = { ...value };
361
+ delete core.requestId;
362
+ delete core.requestDigest;
363
+ if (
364
+ value.requestDigest !== hash(core) ||
365
+ value.requestId !== `skill-transition:${value.requestDigest.slice(7)}`
366
+ ) {
367
+ fail(
368
+ SKILL_REGISTRY_TRANSITION_CORRUPT_CODE,
369
+ "stored transition request digest is invalid",
370
+ );
371
+ }
372
+ return deepFreeze(value);
373
+ }
374
+
375
+ function verifyAttempt(value, descriptor) {
376
+ assertExactRecord(value, ATTEMPT_KEYS, "stored transition attempt");
377
+ if (
378
+ value.schema !== SKILL_REGISTRY_TRANSITION_ATTEMPT_SCHEMA ||
379
+ value.tenantId !== descriptor.tenantId ||
380
+ value.streamId !== descriptor.streamId ||
381
+ !REQUEST_ID.test(value.requestId ?? "") ||
382
+ !DIGEST.test(value.requestDigest ?? "") ||
383
+ !ATTEMPT_ID.test(value.attemptId ?? "") ||
384
+ !DIGEST.test(value.attemptDigest ?? "") ||
385
+ !DIGEST.test(value.candidateId ?? "") ||
386
+ !SKILL_NAME.test(value.skillName ?? "") ||
387
+ !Number.isSafeInteger(value.ordinal) ||
388
+ value.ordinal < 1
389
+ ) {
390
+ fail(
391
+ SKILL_REGISTRY_TRANSITION_CORRUPT_CODE,
392
+ "stored transition attempt identity is invalid",
393
+ );
394
+ }
395
+ normalizeMatrixContext(value.matrixContext);
396
+ const mutationRequest = verifySkillMutationRequest(value.mutationRequest);
397
+ timestamp(value.createdAt, "attempt createdAt");
398
+ const core = { ...value };
399
+ delete core.attemptId;
400
+ delete core.attemptDigest;
401
+ if (
402
+ value.attemptDigest !== hash(core) ||
403
+ value.attemptId !==
404
+ `skill-transition-attempt:${value.attemptDigest.slice(7)}` ||
405
+ mutationRequest.tenantId !== descriptor.tenantId ||
406
+ mutationRequest.skillName !== value.skillName ||
407
+ mutationRequest.operation !== SKILL_MUTATION_OPERATIONS.PROMOTE ||
408
+ mutationRequest.targetScope !== SKILL_MUTATION_TARGET_SCOPES.ACTIVE
409
+ ) {
410
+ fail(
411
+ SKILL_REGISTRY_TRANSITION_CORRUPT_CODE,
412
+ "stored transition attempt binding is invalid",
413
+ );
414
+ }
415
+ return deepFreeze(value);
416
+ }
417
+
418
+ function verifySettlement(value, descriptor) {
419
+ assertExactRecord(value, SETTLEMENT_KEYS, "stored transition settlement");
420
+ if (
421
+ value.schema !== SKILL_REGISTRY_TRANSITION_SETTLEMENT_SCHEMA ||
422
+ value.tenantId !== descriptor.tenantId ||
423
+ value.streamId !== descriptor.streamId ||
424
+ !REQUEST_ID.test(value.requestId ?? "") ||
425
+ !DIGEST.test(value.requestDigest ?? "") ||
426
+ !ATTEMPT_ID.test(value.attemptId ?? "") ||
427
+ !DIGEST.test(value.attemptDigest ?? "") ||
428
+ !DIGEST.test(value.candidateId ?? "") ||
429
+ !SKILL_NAME.test(value.skillName ?? "") ||
430
+ !DIGEST.test(value.activeReleaseDigest ?? "") ||
431
+ !DIGEST.test(value.authorityReceiptDigest ?? "") ||
432
+ !DIGEST.test(value.mutationRequestDigest ?? "") ||
433
+ !DIGEST.test(value.transitionSubjectDigest ?? "") ||
434
+ !DIGEST.test(value.transactionId ?? "") ||
435
+ !DIGEST.test(value.stateDigest ?? "") ||
436
+ !DIGEST.test(value.settlementDigest ?? "") ||
437
+ !Number.isSafeInteger(value.revision) ||
438
+ value.revision < 1
439
+ ) {
440
+ fail(
441
+ SKILL_REGISTRY_TRANSITION_CORRUPT_CODE,
442
+ "stored transition settlement identity is invalid",
443
+ );
444
+ }
445
+ timestamp(value.settledAt, "settlement settledAt");
446
+ const core = { ...value };
447
+ delete core.settlementDigest;
448
+ if (value.settlementDigest !== hash(core)) {
449
+ fail(
450
+ SKILL_REGISTRY_TRANSITION_CORRUPT_CODE,
451
+ "stored transition settlement digest is invalid",
452
+ );
453
+ }
454
+ return deepFreeze(value);
455
+ }
456
+
457
+ function parseRecord(resolution, descriptor, expectedType) {
458
+ if (
459
+ resolution?.schema !== EVOLUTION_ARTIFACT_RESOLUTION_SCHEMA ||
460
+ resolution.authenticated !== true ||
461
+ resolution.found !== true ||
462
+ !Buffer.isBuffer(resolution.bytes)
463
+ ) {
464
+ fail(
465
+ SKILL_REGISTRY_TRANSITION_CORRUPT_CODE,
466
+ "transition artifact resolution is unauthenticated or incomplete",
467
+ );
468
+ }
469
+ let record;
470
+ try {
471
+ record = JSON.parse(resolution.bytes.toString("utf8"));
472
+ } catch {
473
+ fail(
474
+ SKILL_REGISTRY_TRANSITION_CORRUPT_CODE,
475
+ "transition artifact is not canonical JSON",
476
+ );
477
+ }
478
+ if (
479
+ record?.schema !== EVOLUTION_DURABLE_ARTIFACT_RECORD_SCHEMA ||
480
+ record.tenantId !== descriptor.artifactTenantId ||
481
+ record.audience !== descriptor.audience ||
482
+ record.purpose !== descriptor.purpose ||
483
+ record.retention !== "ledger" ||
484
+ record.type !== expectedType
485
+ ) {
486
+ fail(
487
+ SKILL_REGISTRY_TRANSITION_CORRUPT_CODE,
488
+ "transition durable artifact binding is invalid",
489
+ );
490
+ }
491
+ return record.value;
492
+ }
493
+
494
+ function confirmPublished(published, label) {
495
+ if (
496
+ published?.receipt?.persisted !== true ||
497
+ published.receipt.readbackVerified !== true ||
498
+ published.receipt.integrityVerified !== true ||
499
+ published.receipt.retention !== "ledger"
500
+ ) {
501
+ fail(
502
+ SKILL_REGISTRY_TRANSITION_CORRUPT_CODE,
503
+ `${label} was not durably read back`,
504
+ );
505
+ }
506
+ return published;
507
+ }
508
+
509
+ export class SkillRegistryTransitionLedgerAdapter {
510
+ constructor({
511
+ descriptor: input,
512
+ artifactPorts,
513
+ ledger,
514
+ ledgerArtifactResolver,
515
+ sourceVerifier,
516
+ candidateRegistry,
517
+ releaseRegistry,
518
+ authority,
519
+ controlPlane,
520
+ now = Date.now,
521
+ crashHook = null,
522
+ } = {}) {
523
+ this.descriptor = normalizeDescriptor(input);
524
+ this._put = capture(artifactPorts, "putCanonical", "artifactPorts");
525
+ this._readLedger = capture(ledger, "read", "ledger");
526
+ this._verifyLedger = capture(ledger, "verify", "ledger");
527
+ this._appendLedger = capture(ledger, "appendDomainEvent", "ledger");
528
+ this._verifySource = capture(
529
+ sourceVerifier,
530
+ "verify",
531
+ "transition source verifier",
532
+ );
533
+ this._readCandidate = capture(
534
+ candidateRegistry,
535
+ "read",
536
+ "candidate registry",
537
+ );
538
+ this._readState = capture(releaseRegistry, "readState", "release registry");
539
+ this._readRelease = capture(
540
+ releaseRegistry,
541
+ "readRelease",
542
+ "release registry",
543
+ );
544
+ if (
545
+ candidateRegistry?.tenantId !== this.descriptor.tenantId ||
546
+ releaseRegistry?.tenantId !== this.descriptor.tenantId
547
+ ) {
548
+ throw new TypeError("transition registries must match descriptor tenant");
549
+ }
550
+ if (!(authority instanceof SkillMutationAuthority)) {
551
+ throw new TypeError("SkillMutationAuthority is required");
552
+ }
553
+ this._authorize = authority.authorize.bind(authority);
554
+ const capturedControlPlane =
555
+ captureSkillEvaluatedPromotionControlPlane(controlPlane);
556
+ if (capturedControlPlane.tenantId !== this.descriptor.tenantId) {
557
+ throw new TypeError("transition control plane belongs to another tenant");
558
+ }
559
+ this._promoteEvaluated = capturedControlPlane.promoteEvaluated;
560
+ if (!isEvolutionLedgerArtifactResolver(ledgerArtifactResolver)) {
561
+ throw new TypeError(
562
+ "a branded EvolutionArtifactPorts ledger resolver is required",
563
+ );
564
+ }
565
+ if (typeof now !== "function")
566
+ throw new TypeError("now must be a function");
567
+ if (crashHook !== null && typeof crashHook !== "function") {
568
+ throw new TypeError("crashHook must be a function or null");
569
+ }
570
+ this._resolveArtifact = ledgerArtifactResolver;
571
+ this._now = now;
572
+ this._crashHook = crashHook;
573
+ Object.freeze(this);
574
+ }
575
+
576
+ _clock() {
577
+ const nowMs = Number(this._now());
578
+ if (!Number.isFinite(nowMs))
579
+ throw new TypeError("transition clock is invalid");
580
+ return { nowMs, iso: new Date(nowMs).toISOString() };
581
+ }
582
+
583
+ async _crash(phase, value) {
584
+ if (this._crashHook) await this._crashHook(phase, deepFreeze(value));
585
+ }
586
+
587
+ _events(type) {
588
+ const events = this._readLedger();
589
+ if (!Array.isArray(events)) {
590
+ fail(
591
+ SKILL_REGISTRY_TRANSITION_CORRUPT_CODE,
592
+ "EvolutionLedger did not return transition events",
593
+ );
594
+ }
595
+ return events.filter(
596
+ (event) =>
597
+ event.schema === EVOLUTION_LEDGER_DOMAIN_EVENT_SCHEMA &&
598
+ event.type === type &&
599
+ event.tenantId === this.descriptor.tenantId &&
600
+ event.correlationId === this.descriptor.streamId,
601
+ );
602
+ }
603
+
604
+ _resolveEvent(event, expectedType) {
605
+ const identity = this._verifyLedger();
606
+ const resolution = this._resolveArtifact({
607
+ epoch: identity.epoch,
608
+ ledgerId: identity.ledgerId,
609
+ ref: event.subjectRef,
610
+ tenantId: this.descriptor.artifactTenantId,
611
+ });
612
+ if (
613
+ resolution?.ref !== event.subjectRef.ref ||
614
+ resolution.digest !== event.subjectRef.digest
615
+ ) {
616
+ fail(
617
+ SKILL_REGISTRY_TRANSITION_CORRUPT_CODE,
618
+ "transition ledger subject was substituted",
619
+ );
620
+ }
621
+ return parseRecord(resolution, this.descriptor, expectedType);
622
+ }
623
+
624
+ _requests() {
625
+ return this._events(SKILL_REGISTRY_TRANSITION_REQUEST_EVENT_TYPE).map(
626
+ (event) => {
627
+ const request = verifyRequest(
628
+ this._resolveEvent(event, REQUEST_TYPE),
629
+ this.descriptor,
630
+ );
631
+ if (
632
+ event.eventId !==
633
+ `${SKILL_REGISTRY_TRANSITION_REQUEST_EVENT_TYPE}.${request.requestDigest.slice(7)}` ||
634
+ event.decision !== "proposed" ||
635
+ event.skillName !== request.skillName ||
636
+ !Array.isArray(event.sourceRefs) ||
637
+ event.sourceRefs.length !== 0
638
+ ) {
639
+ fail(
640
+ SKILL_REGISTRY_TRANSITION_CORRUPT_CODE,
641
+ "transition request event binding is invalid",
642
+ );
643
+ }
644
+ return { event, request };
645
+ },
646
+ );
647
+ }
648
+
649
+ _attempts() {
650
+ const requests = this._requests();
651
+ return this._events(SKILL_REGISTRY_TRANSITION_ATTEMPT_EVENT_TYPE).map(
652
+ (event) => {
653
+ const attempt = verifyAttempt(
654
+ this._resolveEvent(event, ATTEMPT_TYPE),
655
+ this.descriptor,
656
+ );
657
+ const request = requests.find(
658
+ (entry) => entry.request.requestId === attempt.requestId,
659
+ );
660
+ if (
661
+ !request ||
662
+ attempt.requestDigest !== request.request.requestDigest ||
663
+ attempt.candidateId !== request.request.candidateId ||
664
+ attempt.skillName !== request.request.skillName ||
665
+ !same(attempt.matrixContext, request.request.matrixContext) ||
666
+ event.eventId !==
667
+ `${SKILL_REGISTRY_TRANSITION_ATTEMPT_EVENT_TYPE}.${attempt.attemptDigest.slice(7)}` ||
668
+ event.decision !== "prepared" ||
669
+ event.skillName !== attempt.skillName ||
670
+ !Array.isArray(event.sourceRefs) ||
671
+ event.sourceRefs.length !== 1 ||
672
+ !same(event.sourceRefs[0], request.event.subjectRef)
673
+ ) {
674
+ fail(
675
+ SKILL_REGISTRY_TRANSITION_CORRUPT_CODE,
676
+ "transition attempt lineage is invalid",
677
+ );
678
+ }
679
+ return { event, attempt, request };
680
+ },
681
+ );
682
+ }
683
+
684
+ _settlements() {
685
+ const attempts = this._attempts();
686
+ return this._events(SKILL_REGISTRY_TRANSITION_SETTLED_EVENT_TYPE).map(
687
+ (event) => {
688
+ const settlement = verifySettlement(
689
+ this._resolveEvent(event, SETTLEMENT_TYPE),
690
+ this.descriptor,
691
+ );
692
+ const attempt = attempts.find(
693
+ (entry) => entry.attempt.attemptId === settlement.attemptId,
694
+ );
695
+ if (
696
+ !attempt ||
697
+ settlement.requestId !== attempt.request.request.requestId ||
698
+ settlement.requestDigest !== attempt.request.request.requestDigest ||
699
+ settlement.attemptDigest !== attempt.attempt.attemptDigest ||
700
+ event.eventId !==
701
+ `${SKILL_REGISTRY_TRANSITION_SETTLED_EVENT_TYPE}.${settlement.settlementDigest.slice(7)}` ||
702
+ event.decision !== "committed" ||
703
+ event.skillName !== settlement.skillName ||
704
+ !Array.isArray(event.sourceRefs) ||
705
+ event.sourceRefs.length !== 2 ||
706
+ !same(event.sourceRefs[0], attempt.request.event.subjectRef) ||
707
+ !same(event.sourceRefs[1], attempt.event.subjectRef)
708
+ ) {
709
+ fail(
710
+ SKILL_REGISTRY_TRANSITION_CORRUPT_CODE,
711
+ "transition settlement lineage is invalid",
712
+ );
713
+ }
714
+ this._verifyCommittedSettlement(settlement, attempt.attempt);
715
+ return { event, settlement, attempt };
716
+ },
717
+ );
718
+ }
719
+
720
+ _publish(type, value) {
721
+ return confirmPublished(
722
+ this._put(type, value, {
723
+ audience: this.descriptor.audience,
724
+ purpose: this.descriptor.purpose,
725
+ retention: "ledger",
726
+ }),
727
+ type,
728
+ );
729
+ }
730
+
731
+ _append(event, head) {
732
+ const receipt = this._appendLedger(event, {
733
+ expectedHeadDigest: head.headDigest,
734
+ expectedSequence: head.sequence,
735
+ });
736
+ if (
737
+ receipt?.authenticated !== true ||
738
+ receipt.committed !== true ||
739
+ receipt.durable !== true ||
740
+ receipt.eventId !== event.eventId ||
741
+ !DIGEST.test(receipt.receiptDigest ?? "")
742
+ ) {
743
+ fail(
744
+ SKILL_REGISTRY_TRANSITION_CORRUPT_CODE,
745
+ "transition ledger append was not durably authenticated",
746
+ );
747
+ }
748
+ return receipt;
749
+ }
750
+
751
+ async _resolveSource(input) {
752
+ const normalized = normalizeSourceInput(input);
753
+ const verified = await this._verifySource(normalized);
754
+ return normalizeVerifiedSource(verified, this.descriptor, normalized);
755
+ }
756
+
757
+ async _assertSource(request) {
758
+ const current = await this._resolveSource({
759
+ candidateCreatedRef: request.candidateCreatedRef,
760
+ evalCompletedRef: request.evalCompletedRef,
761
+ humanTaskSettledRef: request.humanTaskSettledRef,
762
+ });
763
+ if (!same(current, request)) {
764
+ fail(
765
+ SKILL_REGISTRY_TRANSITION_CONFLICT_CODE,
766
+ "transition source no longer matches its durable request",
767
+ );
768
+ }
769
+ }
770
+
771
+ _assertCandidate(request) {
772
+ const candidate = this._readCandidate(request.candidateId);
773
+ if (
774
+ candidate?.candidateId !== request.candidateId ||
775
+ candidate.tenantId !== this.descriptor.tenantId ||
776
+ candidate.skillName !== request.skillName ||
777
+ !DIGEST.test(candidate.contentDigest ?? "") ||
778
+ !DIGEST.test(candidate.dependencyLockDigest ?? "")
779
+ ) {
780
+ fail(
781
+ SKILL_REGISTRY_TRANSITION_CORRUPT_CODE,
782
+ "transition candidate is missing or substituted",
783
+ );
784
+ }
785
+ return candidate;
786
+ }
787
+
788
+ async enqueue(input) {
789
+ const request = await this._resolveSource(input);
790
+ this._assertCandidate(request);
791
+ const eventId = `${SKILL_REGISTRY_TRANSITION_REQUEST_EVENT_TYPE}.${request.requestDigest.slice(7)}`;
792
+ const existing = this._requests().filter(
793
+ (entry) => entry.event.eventId === eventId,
794
+ );
795
+ if (existing.length > 1) {
796
+ fail(
797
+ SKILL_REGISTRY_TRANSITION_CORRUPT_CODE,
798
+ "transition request is duplicated in the ledger",
799
+ );
800
+ }
801
+ if (existing.length === 1) {
802
+ if (!same(existing[0].request, request)) {
803
+ fail(
804
+ SKILL_REGISTRY_TRANSITION_CONFLICT_CODE,
805
+ "transition request id binds different content",
806
+ );
807
+ }
808
+ return deepFreeze({
809
+ queued: true,
810
+ recovered: true,
811
+ requestId: request.requestId,
812
+ requestDigest: request.requestDigest,
813
+ });
814
+ }
815
+ const head = this._verifyLedger();
816
+ const published = this._publish(REQUEST_TYPE, request);
817
+ try {
818
+ this._append(
819
+ {
820
+ artifactTenantId: this.descriptor.artifactTenantId,
821
+ correlationId: this.descriptor.streamId,
822
+ decision: "proposed",
823
+ eventId,
824
+ reason:
825
+ "CandidateCreated/EvalCompleted/HumanTaskSettled requested promotion",
826
+ skillName: request.skillName,
827
+ sourceRefs: [],
828
+ subjectRef: published.ref,
829
+ tenantId: this.descriptor.tenantId,
830
+ timestamp: request.effectiveAt,
831
+ type: SKILL_REGISTRY_TRANSITION_REQUEST_EVENT_TYPE,
832
+ },
833
+ head,
834
+ );
835
+ } catch (cause) {
836
+ const recovered = this._requests().find(
837
+ (entry) => entry.event.eventId === eventId,
838
+ );
839
+ if (!recovered || !same(recovered.request, request)) throw cause;
840
+ return deepFreeze({
841
+ queued: true,
842
+ recovered: true,
843
+ requestId: request.requestId,
844
+ requestDigest: request.requestDigest,
845
+ });
846
+ }
847
+ return deepFreeze({
848
+ queued: true,
849
+ recovered: false,
850
+ requestId: request.requestId,
851
+ requestDigest: request.requestDigest,
852
+ });
853
+ }
854
+
855
+ _buildAttempt(request, ordinal) {
856
+ const candidate = this._assertCandidate(request);
857
+ const state = this._readState(request.skillName);
858
+ if (
859
+ state?.tenantId !== this.descriptor.tenantId ||
860
+ state.skillName !== request.skillName ||
861
+ !Number.isSafeInteger(state.revision) ||
862
+ state.revision < 0
863
+ ) {
864
+ fail(
865
+ SKILL_REGISTRY_TRANSITION_CORRUPT_CODE,
866
+ "active Skill state is invalid",
867
+ );
868
+ }
869
+ let activeContentDigest = EMPTY_SKILL_ACTIVE_DIGEST;
870
+ if (state.activeReleaseDigest !== null) {
871
+ const active = this._readRelease(state.activeReleaseDigest);
872
+ if (
873
+ active?.tenantId !== this.descriptor.tenantId ||
874
+ active.skillName !== request.skillName ||
875
+ !DIGEST.test(active.contentDigest ?? "")
876
+ ) {
877
+ fail(
878
+ SKILL_REGISTRY_TRANSITION_CORRUPT_CODE,
879
+ "active Skill release is invalid",
880
+ );
881
+ }
882
+ activeContentDigest = active.contentDigest;
883
+ }
884
+ const expectedParent =
885
+ state.activeReleaseDigest === null ? null : activeContentDigest;
886
+ if (candidate.parentDigest !== expectedParent) {
887
+ fail(
888
+ SKILL_REGISTRY_TRANSITION_CONFLICT_CODE,
889
+ "transition candidate is stale for the active Skill parent",
890
+ );
891
+ }
892
+ const { nowMs, iso } = this._clock();
893
+ const operationId = `transition:${request.requestDigest.slice(7, 39)}:${ordinal}`;
894
+ const nonce = `transition_${hash({ requestDigest: request.requestDigest, ordinal }).slice(7, 47)}`;
895
+ const transitionSubjectDigest = digestSkillMutationTransitionSubject({
896
+ tenantId: this.descriptor.tenantId,
897
+ skillName: request.skillName,
898
+ operation: SKILL_MUTATION_OPERATIONS.PROMOTE,
899
+ candidateId: request.candidateId,
900
+ rollbackTargetReleaseDigest: null,
901
+ dependencyLockDigest: candidate.dependencyLockDigest,
902
+ expectedActiveContentDigest: activeContentDigest,
903
+ expectedActiveRevision: state.revision,
904
+ });
905
+ const mutationRequest = buildSkillMutationRequest({
906
+ tenantId: this.descriptor.tenantId,
907
+ audience: this.descriptor.audience,
908
+ operationId,
909
+ operation: SKILL_MUTATION_OPERATIONS.PROMOTE,
910
+ transitionSubjectDigest,
911
+ skillName: request.skillName,
912
+ targetScope: SKILL_MUTATION_TARGET_SCOPES.ACTIVE,
913
+ expectedTargetDigest: activeContentDigest,
914
+ expectedTargetRevision: state.revision,
915
+ expiresAt: new Date(nowMs + AUTHORIZATION_TTL_MS).toISOString(),
916
+ nonce,
917
+ receipts: request.receipts,
918
+ });
919
+ const core = {
920
+ schema: SKILL_REGISTRY_TRANSITION_ATTEMPT_SCHEMA,
921
+ tenantId: this.descriptor.tenantId,
922
+ streamId: this.descriptor.streamId,
923
+ requestId: request.requestId,
924
+ requestDigest: request.requestDigest,
925
+ ordinal,
926
+ candidateId: request.candidateId,
927
+ skillName: request.skillName,
928
+ matrixContext: request.matrixContext,
929
+ mutationRequest,
930
+ createdAt: iso,
931
+ };
932
+ const attemptDigest = hash(core);
933
+ return deepFreeze({
934
+ ...core,
935
+ attemptId: `skill-transition-attempt:${attemptDigest.slice(7)}`,
936
+ attemptDigest,
937
+ });
938
+ }
939
+
940
+ _persistAttempt(requestEntry, attempt) {
941
+ const eventId = `${SKILL_REGISTRY_TRANSITION_ATTEMPT_EVENT_TYPE}.${attempt.attemptDigest.slice(7)}`;
942
+ const existing = this._attempts().filter(
943
+ (entry) => entry.event.eventId === eventId,
944
+ );
945
+ if (existing.length > 1) {
946
+ fail(
947
+ SKILL_REGISTRY_TRANSITION_CORRUPT_CODE,
948
+ "transition attempt is duplicated in the ledger",
949
+ );
950
+ }
951
+ if (existing.length === 1) {
952
+ if (!same(existing[0].attempt, attempt)) {
953
+ fail(
954
+ SKILL_REGISTRY_TRANSITION_CONFLICT_CODE,
955
+ "transition attempt id binds different content",
956
+ );
957
+ }
958
+ return { event: existing[0].event, recovered: true };
959
+ }
960
+ const head = this._verifyLedger();
961
+ const published = this._publish(ATTEMPT_TYPE, attempt);
962
+ try {
963
+ this._append(
964
+ {
965
+ artifactTenantId: this.descriptor.artifactTenantId,
966
+ correlationId: this.descriptor.streamId,
967
+ decision: "prepared",
968
+ eventId,
969
+ reason: `durable promotion attempt ${attempt.ordinal} prepared before authorization`,
970
+ skillName: attempt.skillName,
971
+ sourceRefs: [requestEntry.event.subjectRef],
972
+ subjectRef: published.ref,
973
+ tenantId: this.descriptor.tenantId,
974
+ timestamp: attempt.createdAt,
975
+ type: SKILL_REGISTRY_TRANSITION_ATTEMPT_EVENT_TYPE,
976
+ },
977
+ head,
978
+ );
979
+ } catch (cause) {
980
+ const recovered = this._attempts().find(
981
+ (entry) => entry.event.eventId === eventId,
982
+ );
983
+ if (!recovered || !same(recovered.attempt, attempt)) throw cause;
984
+ return { event: recovered.event, recovered: true };
985
+ }
986
+ const entry = this._attempts().find(
987
+ (candidateEntry) => candidateEntry.event.eventId === eventId,
988
+ );
989
+ if (!entry) {
990
+ fail(
991
+ SKILL_REGISTRY_TRANSITION_CORRUPT_CODE,
992
+ "persisted transition attempt could not be recovered",
993
+ );
994
+ }
995
+ return { event: entry.event, recovered: false };
996
+ }
997
+
998
+ _committedForAttempt(attempt) {
999
+ const request = attempt.mutationRequest;
1000
+ const state = this._readState(attempt.skillName);
1001
+ if (
1002
+ state?.tenantId !== this.descriptor.tenantId ||
1003
+ state.skillName !== attempt.skillName ||
1004
+ state.revision !== request.expectedTargetRevision + 1 ||
1005
+ !DIGEST.test(state.activeReleaseDigest ?? "") ||
1006
+ !DIGEST.test(state.stateDigest ?? "") ||
1007
+ !DIGEST.test(state.transactionId ?? "")
1008
+ ) {
1009
+ return null;
1010
+ }
1011
+ const release = this._readRelease(state.activeReleaseDigest);
1012
+ if (
1013
+ release?.tenantId !== this.descriptor.tenantId ||
1014
+ release.skillName !== attempt.skillName ||
1015
+ release.candidateId !== attempt.candidateId ||
1016
+ release.mutationRequestDigest !== request.requestDigest ||
1017
+ release.transitionSubjectDigest !== request.transitionSubjectDigest ||
1018
+ release.authorityReceiptDigest !== state.authorityReceiptDigest
1019
+ ) {
1020
+ return null;
1021
+ }
1022
+ return deepFreeze({ release, state });
1023
+ }
1024
+
1025
+ _verifyCommittedSettlement(settlement, attempt) {
1026
+ const request = attempt.mutationRequest;
1027
+ const release = this._readRelease(settlement.activeReleaseDigest);
1028
+ if (
1029
+ release?.tenantId !== this.descriptor.tenantId ||
1030
+ release.skillName !== settlement.skillName ||
1031
+ release.candidateId !== settlement.candidateId ||
1032
+ release.authorityReceiptDigest !== settlement.authorityReceiptDigest ||
1033
+ release.mutationRequestDigest !== settlement.mutationRequestDigest ||
1034
+ release.mutationRequestDigest !== request.requestDigest ||
1035
+ release.transitionSubjectDigest !== settlement.transitionSubjectDigest ||
1036
+ release.transitionSubjectDigest !== request.transitionSubjectDigest
1037
+ ) {
1038
+ fail(
1039
+ SKILL_REGISTRY_TRANSITION_CORRUPT_CODE,
1040
+ "transition settlement release binding is invalid",
1041
+ );
1042
+ }
1043
+ const state = this._readState(settlement.skillName);
1044
+ const exactCurrent =
1045
+ state.transactionId === settlement.transactionId &&
1046
+ state.revision === settlement.revision &&
1047
+ state.stateDigest === settlement.stateDigest &&
1048
+ state.activeReleaseDigest === settlement.activeReleaseDigest &&
1049
+ state.authorityReceiptDigest === settlement.authorityReceiptDigest;
1050
+ if (!exactCurrent && state.revision <= settlement.revision) {
1051
+ fail(
1052
+ SKILL_REGISTRY_TRANSITION_CORRUPT_CODE,
1053
+ "transition settlement has no matching or later authenticated state",
1054
+ );
1055
+ }
1056
+ return { release, state };
1057
+ }
1058
+
1059
+ _settle(requestEntry, attemptEntry, committed) {
1060
+ const existing = this._settlements().filter(
1061
+ (entry) => entry.settlement.requestId === requestEntry.request.requestId,
1062
+ );
1063
+ if (existing.length > 1) {
1064
+ fail(
1065
+ SKILL_REGISTRY_TRANSITION_CORRUPT_CODE,
1066
+ "transition request has conflicting settlements",
1067
+ );
1068
+ }
1069
+ if (existing.length === 1) {
1070
+ if (existing[0].settlement.attemptId !== attemptEntry.attempt.attemptId) {
1071
+ fail(
1072
+ SKILL_REGISTRY_TRANSITION_CONFLICT_CODE,
1073
+ "transition request was settled by another attempt",
1074
+ );
1075
+ }
1076
+ return deepFreeze({
1077
+ settlement: existing[0].settlement,
1078
+ recovered: true,
1079
+ });
1080
+ }
1081
+ const { iso } = this._clock();
1082
+ const { release, state } = committed;
1083
+ const core = {
1084
+ schema: SKILL_REGISTRY_TRANSITION_SETTLEMENT_SCHEMA,
1085
+ tenantId: this.descriptor.tenantId,
1086
+ streamId: this.descriptor.streamId,
1087
+ requestId: requestEntry.request.requestId,
1088
+ requestDigest: requestEntry.request.requestDigest,
1089
+ attemptId: attemptEntry.attempt.attemptId,
1090
+ attemptDigest: attemptEntry.attempt.attemptDigest,
1091
+ candidateId: attemptEntry.attempt.candidateId,
1092
+ skillName: attemptEntry.attempt.skillName,
1093
+ activeReleaseDigest: release.releaseDigest,
1094
+ authorityReceiptDigest: release.authorityReceiptDigest,
1095
+ mutationRequestDigest: release.mutationRequestDigest,
1096
+ transitionSubjectDigest: release.transitionSubjectDigest,
1097
+ transactionId: state.transactionId,
1098
+ revision: state.revision,
1099
+ stateDigest: state.stateDigest,
1100
+ settledAt: iso,
1101
+ };
1102
+ const settlement = deepFreeze({ ...core, settlementDigest: hash(core) });
1103
+ this._verifyCommittedSettlement(settlement, attemptEntry.attempt);
1104
+ const eventId = `${SKILL_REGISTRY_TRANSITION_SETTLED_EVENT_TYPE}.${settlement.settlementDigest.slice(7)}`;
1105
+ const head = this._verifyLedger();
1106
+ const published = this._publish(SETTLEMENT_TYPE, settlement);
1107
+ try {
1108
+ this._append(
1109
+ {
1110
+ artifactTenantId: this.descriptor.artifactTenantId,
1111
+ correlationId: this.descriptor.streamId,
1112
+ decision: "committed",
1113
+ eventId,
1114
+ reason: `Skill release ${release.releaseDigest} committed from durable transition`,
1115
+ skillName: settlement.skillName,
1116
+ sourceRefs: [
1117
+ requestEntry.event.subjectRef,
1118
+ attemptEntry.event.subjectRef,
1119
+ ],
1120
+ subjectRef: published.ref,
1121
+ tenantId: this.descriptor.tenantId,
1122
+ timestamp: settlement.settledAt,
1123
+ type: SKILL_REGISTRY_TRANSITION_SETTLED_EVENT_TYPE,
1124
+ },
1125
+ head,
1126
+ );
1127
+ } catch (cause) {
1128
+ const recovered = this._settlements().find(
1129
+ (entry) =>
1130
+ entry.settlement.settlementDigest === settlement.settlementDigest,
1131
+ );
1132
+ if (!recovered || !same(recovered.settlement, settlement)) throw cause;
1133
+ return deepFreeze({ settlement, recovered: true });
1134
+ }
1135
+ return deepFreeze({ settlement, recovered: false });
1136
+ }
1137
+
1138
+ list() {
1139
+ const settlements = this._settlements();
1140
+ const attempts = this._attempts();
1141
+ return deepFreeze(
1142
+ this._requests()
1143
+ .map((requestEntry) => {
1144
+ const requestAttempts = attempts
1145
+ .filter(
1146
+ (entry) =>
1147
+ entry.attempt.requestId === requestEntry.request.requestId,
1148
+ )
1149
+ .sort(
1150
+ (left, right) => left.attempt.ordinal - right.attempt.ordinal,
1151
+ );
1152
+ const requestSettlements = settlements.filter(
1153
+ (entry) =>
1154
+ entry.settlement.requestId === requestEntry.request.requestId,
1155
+ );
1156
+ if (requestSettlements.length > 1) {
1157
+ fail(
1158
+ SKILL_REGISTRY_TRANSITION_CORRUPT_CODE,
1159
+ "transition request has multiple settlements",
1160
+ );
1161
+ }
1162
+ return {
1163
+ request: requestEntry.request,
1164
+ requestEventSequence: requestEntry.event.sequence,
1165
+ attempts: requestAttempts.map((entry) => entry.attempt),
1166
+ status: requestSettlements.length === 1 ? "committed" : "pending",
1167
+ settlement: requestSettlements[0]?.settlement ?? null,
1168
+ };
1169
+ })
1170
+ .sort(
1171
+ (left, right) =>
1172
+ left.requestEventSequence - right.requestEventSequence,
1173
+ ),
1174
+ );
1175
+ }
1176
+
1177
+ async processNext() {
1178
+ const pending = this.list().find((entry) => entry.status === "pending");
1179
+ if (!pending) return deepFreeze({ processed: false });
1180
+ const requestEntry = this._requests().find(
1181
+ (entry) => entry.request.requestId === pending.request.requestId,
1182
+ );
1183
+ await this._assertSource(requestEntry.request);
1184
+ this._assertCandidate(requestEntry.request);
1185
+
1186
+ const attemptEntries = this._attempts()
1187
+ .filter(
1188
+ (entry) => entry.attempt.requestId === requestEntry.request.requestId,
1189
+ )
1190
+ .sort((left, right) => right.attempt.ordinal - left.attempt.ordinal);
1191
+ for (const attemptEntry of attemptEntries) {
1192
+ const committed = this._committedForAttempt(attemptEntry.attempt);
1193
+ if (committed) {
1194
+ const settled = this._settle(requestEntry, attemptEntry, committed);
1195
+ return deepFreeze({
1196
+ processed: true,
1197
+ recovered: true,
1198
+ requestId: requestEntry.request.requestId,
1199
+ attemptId: attemptEntry.attempt.attemptId,
1200
+ releaseDigest: settled.settlement.activeReleaseDigest,
1201
+ revision: settled.settlement.revision,
1202
+ });
1203
+ }
1204
+ }
1205
+
1206
+ const { nowMs } = this._clock();
1207
+ const latest = attemptEntries[0] ?? null;
1208
+ if (
1209
+ latest &&
1210
+ Date.parse(latest.attempt.mutationRequest.expiresAt) > nowMs
1211
+ ) {
1212
+ return deepFreeze({
1213
+ processed: false,
1214
+ inFlight: true,
1215
+ requestId: requestEntry.request.requestId,
1216
+ attemptId: latest.attempt.attemptId,
1217
+ retryAt: latest.attempt.mutationRequest.expiresAt,
1218
+ });
1219
+ }
1220
+
1221
+ const attempt = this._buildAttempt(
1222
+ requestEntry.request,
1223
+ (latest?.attempt.ordinal ?? 0) + 1,
1224
+ );
1225
+ const persisted = this._persistAttempt(requestEntry, attempt);
1226
+ const attemptEntry = { event: persisted.event, attempt };
1227
+ await this._crash("after-attempt", {
1228
+ requestId: requestEntry.request.requestId,
1229
+ attemptId: attempt.attemptId,
1230
+ });
1231
+ const capability = await this._authorize(attempt.mutationRequest);
1232
+ await this._crash("after-authorization", {
1233
+ requestId: requestEntry.request.requestId,
1234
+ attemptId: attempt.attemptId,
1235
+ });
1236
+
1237
+ let result;
1238
+ try {
1239
+ result = await this._promoteEvaluated({
1240
+ authorization: { capability, request: attempt.mutationRequest },
1241
+ candidateId: attempt.candidateId,
1242
+ matrixContext: attempt.matrixContext,
1243
+ });
1244
+ } catch (cause) {
1245
+ const recovered = this._committedForAttempt(attempt);
1246
+ if (!recovered) throw cause;
1247
+ result = recovered;
1248
+ }
1249
+ const committed = this._committedForAttempt(attempt);
1250
+ if (
1251
+ !committed ||
1252
+ result.release?.releaseDigest !== committed.release.releaseDigest ||
1253
+ result.state?.stateDigest !== committed.state.stateDigest
1254
+ ) {
1255
+ fail(
1256
+ SKILL_REGISTRY_TRANSITION_CORRUPT_CODE,
1257
+ "promotion control plane did not commit the exact durable attempt",
1258
+ );
1259
+ }
1260
+ await this._crash("after-registry-commit", {
1261
+ requestId: requestEntry.request.requestId,
1262
+ attemptId: attempt.attemptId,
1263
+ releaseDigest: committed.release.releaseDigest,
1264
+ stateDigest: committed.state.stateDigest,
1265
+ });
1266
+ const settled = this._settle(requestEntry, attemptEntry, committed);
1267
+ return deepFreeze({
1268
+ processed: true,
1269
+ recovered: persisted.recovered || settled.recovered,
1270
+ requestId: requestEntry.request.requestId,
1271
+ attemptId: attempt.attemptId,
1272
+ releaseDigest: settled.settlement.activeReleaseDigest,
1273
+ revision: settled.settlement.revision,
1274
+ });
1275
+ }
1276
+ }
1277
+
1278
+ export function createSkillRegistryTransitionLedgerAdapter(options) {
1279
+ return new SkillRegistryTransitionLedgerAdapter(options);
1280
+ }