chainlesschain 0.166.15 → 0.166.16

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 (184) hide show
  1. package/package.json +1 -1
  2. package/src/assets/web-panel/assets/{AIOps-To-6rimH.js → AIOps-CTxa2Wbq.js} +1 -1
  3. package/src/assets/web-panel/assets/{ActionButton-CpdCnTv3.js → ActionButton-DHrQyYhy.js} +1 -1
  4. package/src/assets/web-panel/assets/{Analytics-DK7VNES_.js → Analytics-DQiUqcNl.js} +3 -3
  5. package/src/assets/web-panel/assets/{AppLayout-NZ5FvhCx.js → AppLayout-_Iz4r6wt.js} +5 -5
  6. package/src/assets/web-panel/assets/{Artifacts-NXceQPmN.js → Artifacts-BNs63YHo.js} +1 -1
  7. package/src/assets/web-panel/assets/{Audit-B2d64DH2.js → Audit-BgHIqRLn.js} +1 -1
  8. package/src/assets/web-panel/assets/{BackgroundAgents-ByYgvEVY.js → BackgroundAgents-DUQ5axBB.js} +1 -1
  9. package/src/assets/web-panel/assets/{Backup-BXvQLDvN.js → Backup-CJ51E9Vw.js} +1 -1
  10. package/src/assets/web-panel/assets/{BaseInput-C4GZk-As.js → BaseInput-o-5toA_R.js} +1 -1
  11. package/src/assets/web-panel/assets/{Chat-CvPuohL8.js → Chat-Cu7a-x1L.js} +6 -6
  12. package/src/assets/web-panel/assets/{ChatBubbleRenderer-DTftcclX.js → ChatBubbleRenderer-BUyYkAXr.js} +1 -1
  13. package/src/assets/web-panel/assets/{Checkbox-C8dQGIqD.js → Checkbox-DwdmZ71L.js} +1 -1
  14. package/src/assets/web-panel/assets/{Codegen-DRM6S1W4.js → Codegen-BrH2eYiP.js} +1 -1
  15. package/src/assets/web-panel/assets/{Col-BDM8l-r5.js → Col-BVJQEP2j.js} +1 -1
  16. package/src/assets/web-panel/assets/{Community-Bvmfi1fX.js → Community-FGO3vZPq.js} +1 -1
  17. package/src/assets/web-panel/assets/{Compact-DGQTCima.js → Compact-DYGAFVnT.js} +1 -1
  18. package/src/assets/web-panel/assets/{Compliance-D_0C-2P6.js → Compliance-CRvc8Bdt.js} +1 -1
  19. package/src/assets/web-panel/assets/{Cowork-3C_q99ok.js → Cowork-D6Ah2m7A.js} +3 -3
  20. package/src/assets/web-panel/assets/{Cron-CIiMQ1Hl.js → Cron-AXbU2ifR.js} +2 -2
  21. package/src/assets/web-panel/assets/{Crosschain-peebGnES.js → Crosschain-C5DXgEFd.js} +1 -1
  22. package/src/assets/web-panel/assets/{DID-OkX4KB9T.js → DID-Bxh9QV9v.js} +2 -2
  23. package/src/assets/web-panel/assets/{Dashboard-D9lm0Wxa.js → Dashboard-B-ZHEta-.js} +2 -2
  24. package/src/assets/web-panel/assets/{Dropdown-CitActXx.js → Dropdown-66Ik1jqa.js} +1 -1
  25. package/src/assets/web-panel/assets/{EmailListRenderer-DSmFkNKV.js → EmailListRenderer-CCWk1BIM.js} +1 -1
  26. package/src/assets/web-panel/assets/{FamilyGuardDashboard-CBWAi8XL.js → FamilyGuardDashboard--9faIULK.js} +1 -1
  27. package/src/assets/web-panel/assets/{Federation-afj8jDcp.js → Federation-CejW9CgX.js} +1 -1
  28. package/src/assets/web-panel/assets/{FormItemContext-Dabt59KY.js → FormItemContext-acHgcXki.js} +1 -1
  29. package/src/assets/web-panel/assets/{GenericCardRenderer-BQpXA9kj.js → GenericCardRenderer-D2ghd_K4.js} +1 -1
  30. package/src/assets/web-panel/assets/{Git-D2p2Moo1.js → Git-BOe1YJrV.js} +2 -2
  31. package/src/assets/web-panel/assets/{Governance-DkFfPykv.js → Governance-BjgzJsof.js} +1 -1
  32. package/src/assets/web-panel/assets/{Inference-C1DKpeN2.js → Inference-CXCD5rnP.js} +1 -1
  33. package/src/assets/web-panel/assets/{KnowledgeGraph-CjfVg4oQ.js → KnowledgeGraph-CHA8QgJT.js} +1 -1
  34. package/src/assets/web-panel/assets/{Logs-D-zMvdSf.js → Logs-DORPMcL6.js} +2 -2
  35. package/src/assets/web-panel/assets/{MarkdownRenderer-jzEpGkbc.js → MarkdownRenderer-CPPHz01z.js} +1 -1
  36. package/src/assets/web-panel/assets/{Marketplace-DfAjJLyS.js → Marketplace-BCFlo1Yx.js} +1 -1
  37. package/src/assets/web-panel/assets/{McpTools-BGDHQES1.js → McpTools-Dl_uZAKh.js} +3 -3
  38. package/src/assets/web-panel/assets/{Memory-BYqrGAPS.js → Memory-DupHamwz.js} +2 -2
  39. package/src/assets/web-panel/assets/{MobileBridge-CQ6FEtty.js → MobileBridge-BRSMPJxW.js} +1 -1
  40. package/src/assets/web-panel/assets/{MobileProjects-BpMvh5JW.js → MobileProjects-BHpuzsDl.js} +1 -1
  41. package/src/assets/web-panel/assets/{Mtc-CWH25SrU.js → Mtc-Ch0jOQfZ.js} +2 -2
  42. package/src/assets/web-panel/assets/{MtcAudit-D6DiLmei.js → MtcAudit-qzwQrDi7.js} +2 -2
  43. package/src/assets/web-panel/assets/{Multisig-BSxRN4jD.js → Multisig-B5wHzUcm.js} +3 -3
  44. package/src/assets/web-panel/assets/{NLProgramming-C0FDPKT4.js → NLProgramming-Hy9uAiA0.js} +1 -1
  45. package/src/assets/web-panel/assets/{Notes-VKR78bH-.js → Notes-DkuchMC7.js} +3 -3
  46. package/src/assets/web-panel/assets/{NotificationSettings-DLSklSZB.js → NotificationSettings-CYZhD7Pi.js} +1 -1
  47. package/src/assets/web-panel/assets/{OrderTableRenderer-Dpj7D6tC.js → OrderTableRenderer-DKOoqajt.js} +1 -1
  48. package/src/assets/web-panel/assets/{Organization-BB3pB27e.js → Organization-D9AXn6j8.js} +4 -4
  49. package/src/assets/web-panel/assets/{Overflow-Cr7bzwcs.js → Overflow-Bg15J6hX.js} +1 -1
  50. package/src/assets/web-panel/assets/{P2P-D1fkMMzI.js → P2P--B54HoQn.js} +2 -2
  51. package/src/assets/web-panel/assets/{PdhVaultBrowser-BiDEbLNA.js → PdhVaultBrowser-Cz9Kk2Nb.js} +3 -3
  52. package/src/assets/web-panel/assets/{Permissions-c3Ijv0v4.js → Permissions-DHx38eS1.js} +4 -4
  53. package/src/assets/web-panel/assets/{PersonalDataHub-BJwbh3P2.js → PersonalDataHub-Dw8i2hAs.js} +4 -4
  54. package/src/assets/web-panel/assets/{Pipeline-C4fhXafB.js → Pipeline-B2o6Ur2G.js} +1 -1
  55. package/src/assets/web-panel/assets/{Privacy-DzSx3CD-.js → Privacy-8g-h2fnu.js} +1 -1
  56. package/src/assets/web-panel/assets/{ProjectInit-D9fL5fED.js → ProjectInit-DuVTcINx.js} +2 -2
  57. package/src/assets/web-panel/assets/{ProjectSettings-CB5FVrOR.js → ProjectSettings-C8njvdZX.js} +2 -2
  58. package/src/assets/web-panel/assets/Projects-Ca9BAfu7.js +1 -0
  59. package/src/assets/web-panel/assets/{Providers-B9ZH4faS.js → Providers-wFqgod_W.js} +1 -1
  60. package/src/assets/web-panel/assets/{QrScannerModal-BLKDHT8_.js → QrScannerModal-9NCXrpmA.js} +1 -1
  61. package/src/assets/web-panel/assets/{QuickAsk-D1oYUcSY.js → QuickAsk-WUTNvFkY.js} +1 -1
  62. package/src/assets/web-panel/assets/{Recommend-CC2nmrOD.js → Recommend-xT4nYmju.js} +1 -1
  63. package/src/assets/web-panel/assets/{RemoteSession-qW9QF4-r.js → RemoteSession-fak1PBGC.js} +2 -2
  64. package/src/assets/web-panel/assets/{Reputation-BDWqfzcr.js → Reputation-D1AkQvD-.js} +1 -1
  65. package/src/assets/web-panel/assets/{Row-BBV2iCjT.js → Row-DVfm6xGt.js} +1 -1
  66. package/src/assets/web-panel/assets/{RssFeed-CcwrNwZB.js → RssFeed-BDunoLSE.js} +3 -3
  67. package/src/assets/web-panel/assets/{Search-Dr6fZ0D_.js → Search-OPycRoU3.js} +1 -1
  68. package/src/assets/web-panel/assets/{Security-BPzaoCU6.js → Security-C8-Fm3Dt.js} +4 -4
  69. package/src/assets/web-panel/assets/{Services-BoKUU1I_.js → Services-qM_sMpMl.js} +2 -2
  70. package/src/assets/web-panel/assets/{Skeleton-C8lT8GUg.js → Skeleton-D3HBJjVF.js} +1 -1
  71. package/src/assets/web-panel/assets/{Skills-DPpWeicz.js → Skills-jr67X-tP.js} +1 -1
  72. package/src/assets/web-panel/assets/{Sla-UiijzpMU.js → Sla-CS3ggyCg.js} +1 -1
  73. package/src/assets/web-panel/assets/{SpeechSettings-DoofrLwS.js → SpeechSettings-CTOCYxOi.js} +1 -1
  74. package/src/assets/web-panel/assets/{SyncSettings-DXnyfTDw.js → SyncSettings-D3s3cLT5.js} +2 -2
  75. package/src/assets/web-panel/assets/{Tasks-g_WHjKeg.js → Tasks-CdfoJ25m.js} +1 -1
  76. package/src/assets/web-panel/assets/{Templates-DRuCh9g2.js → Templates-Drb4AI8X.js} +1 -1
  77. package/src/assets/web-panel/assets/{Tenant-QHIq1pH1.js → Tenant-DzwR3Qkz.js} +1 -1
  78. package/src/assets/web-panel/assets/{Terminal-G0fuI216.js → Terminal-D-ESq7xp.js} +2 -2
  79. package/src/assets/web-panel/assets/{TimelineRenderer-4MT7SpGk.js → TimelineRenderer-BK-AUuws.js} +1 -1
  80. package/src/assets/web-panel/assets/{Tokens-B5xT5umA.js → Tokens-xGTouuNp.js} +1 -1
  81. package/src/assets/web-panel/assets/{Trigger-D7HPbNgc.js → Trigger-BvZj-v2H.js} +1 -1
  82. package/src/assets/web-panel/assets/{Trust-CxH69dU5.js → Trust-BLC9CGmc.js} +1 -1
  83. package/src/assets/web-panel/assets/{UkeySign-CPPosveH.js → UkeySign-BSuCXpmE.js} +1 -1
  84. package/src/assets/web-panel/assets/{VideoEditing-Du5lGwAt.js → VideoEditing-DT3A-_Ql.js} +1 -1
  85. package/src/assets/web-panel/assets/{Wallet-BBIGNESK.js → Wallet-c1OXao85.js} +4 -4
  86. package/src/assets/web-panel/assets/{WebAuthn-Cc3HSGmv.js → WebAuthn-CuA-F7KJ.js} +5 -5
  87. package/src/assets/web-panel/assets/{WorkflowEditor-DBoVPNCh.js → WorkflowEditor-MANZrf82.js} +1 -1
  88. package/src/assets/web-panel/assets/{chat-DHcjgiem.js → chat-D4Y4Ey0O.js} +1 -1
  89. package/src/assets/web-panel/assets/{colors-DrRrvJIV.js → colors-DDH06hKO.js} +1 -1
  90. package/src/assets/web-panel/assets/{compact-item-BuY_sAoN.js → compact-item-Cym756Xj.js} +1 -1
  91. package/src/assets/web-panel/assets/{createContext-CepH1Fha.js → createContext-By4Nb1k-.js} +1 -1
  92. package/src/assets/web-panel/assets/devWarning-KO3pAGeO.js +1 -0
  93. package/src/assets/web-panel/assets/{hasIn-Dh80Ot6o.js → hasIn-Ch7OY258.js} +1 -1
  94. package/src/assets/web-panel/assets/{index-BW9Pycxh.js → index-6-2eYBXr.js} +1 -1
  95. package/src/assets/web-panel/assets/{index-D7lstbTh.js → index-6CpOV3Pn.js} +1 -1
  96. package/src/assets/web-panel/assets/{index-CUnd5hyp.js → index-ArMrnx0v.js} +1 -1
  97. package/src/assets/web-panel/assets/{index-B0rvJjId.js → index-B51otVoZ.js} +1 -1
  98. package/src/assets/web-panel/assets/{index-D6tOVlq-.js → index-BBBUn5v7.js} +1 -1
  99. package/src/assets/web-panel/assets/{index-NS9dW1F1.js → index-BFydNU6a.js} +1 -1
  100. package/src/assets/web-panel/assets/{index-CiUx-tkV.js → index-BIrRX-ra.js} +1 -1
  101. package/src/assets/web-panel/assets/{index-By0qqC3S.js → index-BZB9q-Lp.js} +1 -1
  102. package/src/assets/web-panel/assets/index-Bh4Rgkg2.js +1 -0
  103. package/src/assets/web-panel/assets/{index-CNQK9DIr.js → index-Bo20BfdS.js} +1 -1
  104. package/src/assets/web-panel/assets/{index-C10033eO.js → index-CF3Lhyns.js} +1 -1
  105. package/src/assets/web-panel/assets/{index-DRy3_beb.js → index-CGsvEbbq.js} +1 -1
  106. package/src/assets/web-panel/assets/{index-DWANFMlq.js → index-CJPks33Y.js} +1 -1
  107. package/src/assets/web-panel/assets/{index-CnRK8_Dd.js → index-CT-3FYwd.js} +1 -1
  108. package/src/assets/web-panel/assets/{index-BPDEOOlA.js → index-CUSUzaB8.js} +1 -1
  109. package/src/assets/web-panel/assets/{index-DYN49m7k.js → index-CV1UC4zo.js} +1 -1
  110. package/src/assets/web-panel/assets/{index-C5vhNRqS.js → index-CV8qXTvb.js} +1 -1
  111. package/src/assets/web-panel/assets/{index-9crgJ3Id.js → index-CZn6dmZe.js} +1 -1
  112. package/src/assets/web-panel/assets/{index-DkZP_QaB.js → index-CjNUTBwe.js} +1 -1
  113. package/src/assets/web-panel/assets/{index-BoRy2yup.js → index-CpudmDhf.js} +1 -1
  114. package/src/assets/web-panel/assets/{index-Ca5d64IH.js → index-CykROEJA.js} +1 -1
  115. package/src/assets/web-panel/assets/{index-A5lVwYVq.js → index-D5sADPGY.js} +1 -1
  116. package/src/assets/web-panel/assets/{index-BnVIvc15.js → index-D9le0-dc.js} +1 -1
  117. package/src/assets/web-panel/assets/{index-Cx21_p3m.js → index-DAkk1M7x.js} +1 -1
  118. package/src/assets/web-panel/assets/{index-0TYLnd11.js → index-DNEJzc0r.js} +1 -1
  119. package/src/assets/web-panel/assets/{index-B9Gh-SOn.js → index-DNlxihs8.js} +1 -1
  120. package/src/assets/web-panel/assets/{index-BtqigRzf.js → index-DiewptTL.js} +1 -1
  121. package/src/assets/web-panel/assets/{index-CW_23-gU.js → index-DjwgJ5Jd.js} +1 -1
  122. package/src/assets/web-panel/assets/index-Dox_coTY.js +1 -0
  123. package/src/assets/web-panel/assets/{index-LBkSOwky.js → index-DtGValNi.js} +1 -1
  124. package/src/assets/web-panel/assets/{index-D5_K3GKu.js → index-DvnywwUj.js} +1 -1
  125. package/src/assets/web-panel/assets/{index-BiZQooWK.js → index-OU8DZ9BL.js} +1 -1
  126. package/src/assets/web-panel/assets/{index-CYydOga6.js → index-XGeQMvWK.js} +1 -1
  127. package/src/assets/web-panel/assets/{index-hYaigdGm.js → index-ZY2Qs_Sn.js} +1 -1
  128. package/src/assets/web-panel/assets/{index-zGH9zH3w.js → index-jx7kr6mB.js} +3 -3
  129. package/src/assets/web-panel/assets/{index-DuRYBWrj.js → index-mL3nOcH6.js} +1 -1
  130. package/src/assets/web-panel/assets/{index-DuSUCH7h.js → index-oBsfUbyH.js} +1 -1
  131. package/src/assets/web-panel/assets/{index-CEfVkKIY.js → index-q-s04OKn.js} +1 -1
  132. package/src/assets/web-panel/assets/{index-DUNyTmkS.js → index-tZ5BUdad.js} +1 -1
  133. package/src/assets/web-panel/assets/{initDefaultProps-nI_X3wKl.js → initDefaultProps-DGcX9iPP.js} +1 -1
  134. package/src/assets/web-panel/assets/{motion-LUHbQu_R.js → motion-2SeL1D8A.js} +1 -1
  135. package/src/assets/web-panel/assets/{move-DHI_KvJt.js → move-3cYbQCpS.js} +1 -1
  136. package/src/assets/web-panel/assets/{mtc-parser-AYdL_7Mo.js → mtc-parser-IDdxfw0z.js} +1 -1
  137. package/src/assets/web-panel/assets/{omit-CtV3HRKa.js → omit-BNswdKO6.js} +1 -1
  138. package/src/assets/web-panel/assets/{pickAttrs-CcJLTOu4.js → pickAttrs-BwCnem2I.js} +1 -1
  139. package/src/assets/web-panel/assets/{placementArrow-DHbiLUps.js → placementArrow-B9PtZ5rq.js} +1 -1
  140. package/src/assets/web-panel/assets/{responsiveObserve-BC2DGkWP.js → responsiveObserve-CPB6iSzV.js} +1 -1
  141. package/src/assets/web-panel/assets/{slide-B4ga4pPY.js → slide-DZgyblZa.js} +1 -1
  142. package/src/assets/web-panel/assets/{statusUtils-hzphgKfH.js → statusUtils-D82IBdsx.js} +1 -1
  143. package/src/assets/web-panel/assets/{styleChecker-IfN9Bxoi.js → styleChecker-qSHtosM-.js} +1 -1
  144. package/src/assets/web-panel/assets/{useFlexGapSupport-pcGKJ4pT.js → useFlexGapSupport-CmWO7h0I.js} +1 -1
  145. package/src/assets/web-panel/assets/{useFs-COXhQpTG.js → useFs-yLrs_ub4.js} +1 -1
  146. package/src/assets/web-panel/assets/{usePersonalDataHub-xp9F9szv.js → usePersonalDataHub-CJ1VW6rh.js} +1 -1
  147. package/src/assets/web-panel/assets/{vnode-BdSQy1Ve.js → vnode-CzY_moQ4.js} +1 -1
  148. package/src/assets/web-panel/assets/{zoom-CHCt5P55.js → zoom-DAa7rEY5.js} +1 -1
  149. package/src/assets/web-panel/index.html +1 -1
  150. package/src/commands/learning.js +71 -1
  151. package/src/commands/team.js +5 -0
  152. package/src/data/changelog.json +15 -2
  153. package/src/lib/agent-team/task-lease.js +12 -1
  154. package/src/lib/capability-manifest.js +212 -1
  155. package/src/lib/evolution/evolution-artifact-ports.js +3277 -0
  156. package/src/lib/evolution/evolution-eval-gate.js +6442 -0
  157. package/src/lib/evolution/evolution-evidence-projector.js +3786 -0
  158. package/src/lib/evolution/evolution-ledger-ports.js +2689 -0
  159. package/src/lib/evolution/evolution-ledger.js +4280 -0
  160. package/src/lib/evolution/skill-candidate-registry.js +2258 -0
  161. package/src/lib/evolution/skill-execution-manifest.js +942 -0
  162. package/src/lib/evolution/skill-mutation-authority.js +1677 -0
  163. package/src/lib/evolution/skill-promotion-controller.js +506 -0
  164. package/src/lib/evolution/skill-release-registry.js +3277 -0
  165. package/src/lib/evolution/skill-target-matrix-eval.js +3233 -0
  166. package/src/lib/evolution/skill-writer-inventory-manifest.js +998 -0
  167. package/src/lib/evolution/skill-writer-inventory-validator.js +816 -0
  168. package/src/lib/formal-quality-eval-runtime.js +100 -0
  169. package/src/lib/graph-kernel/cutover-contract.js +7 -0
  170. package/src/lib/graph-kernel/cutover-ledger.js +8 -7
  171. package/src/lib/graph-kernel/evidence-digest.js +47 -0
  172. package/src/lib/graph-kernel/graph-runtime-surfaces.json +8 -1
  173. package/src/lib/graph-kernel/production-cutover-evidence.js +342 -15
  174. package/src/lib/graph-kernel/production-source-evidence.js +1599 -0
  175. package/src/lib/graph-kernel/retirement-evidence.js +15 -5
  176. package/src/lib/graph-kernel/runtime-surface-manifest.js +67 -2
  177. package/src/lib/headless-manifest.js +3 -1
  178. package/src/lib/learning/skill-improver.js +311 -43
  179. package/src/lib/learning/skill-synthesizer.js +236 -16
  180. package/src/lib/secure-fs.js +20 -0
  181. package/src/assets/web-panel/assets/Projects-DWhwdgfY.js +0 -1
  182. package/src/assets/web-panel/assets/devWarning-DIb8j04q.js +0 -1
  183. package/src/assets/web-panel/assets/index-BJc3t4PC.js +0 -1
  184. package/src/assets/web-panel/assets/index-CkksSUnI.js +0 -1
@@ -0,0 +1,506 @@
1
+ import crypto from "node:crypto";
2
+ import { types as utilTypes } from "node:util";
3
+ import {
4
+ SKILL_MUTATION_OPERATIONS,
5
+ SKILL_MUTATION_RECEIPT_KINDS,
6
+ SKILL_MUTATION_ROLES,
7
+ SKILL_MUTATION_TARGET_SCOPES,
8
+ SkillMutationAuthority,
9
+ buildSkillMutationConsumeContext,
10
+ digestSkillMutationReceiptEnvelope,
11
+ digestSkillMutationTransitionSubject,
12
+ verifySkillMutationConsumptionReceipt,
13
+ verifySkillMutationRequest,
14
+ } from "./skill-mutation-authority.js";
15
+
16
+ const DIGEST_PATTERN = /^sha256:[a-f0-9]{64}$/u;
17
+ const EMPTY_ACTIVE_DOMAIN = "chainlesschain.skill-active/empty/v1\0";
18
+
19
+ /** The CAS digest used by mutation authority when a Skill has no active release. */
20
+ export const EMPTY_SKILL_ACTIVE_DIGEST = `sha256:${crypto
21
+ .createHash("sha256")
22
+ .update(EMPTY_ACTIVE_DOMAIN, "utf8")
23
+ .digest("hex")}`;
24
+
25
+ const TRANSITION_CAPABILITIES = new WeakMap();
26
+
27
+ export class SkillPromotionControllerError extends Error {
28
+ constructor(code, message, details = {}) {
29
+ super(message, details.cause ? { cause: details.cause } : undefined);
30
+ this.name = "SkillPromotionControllerError";
31
+ this.code = code;
32
+ for (const [key, value] of Object.entries(details)) {
33
+ if (key !== "cause") this[key] = value;
34
+ }
35
+ }
36
+ }
37
+
38
+ function failure(code, message, details = {}) {
39
+ return new SkillPromotionControllerError(code, message, details);
40
+ }
41
+
42
+ function deepFreeze(value, seen = new WeakSet()) {
43
+ if (!value || typeof value !== "object" || seen.has(value)) return value;
44
+ seen.add(value);
45
+ for (const key of Reflect.ownKeys(value)) {
46
+ const descriptor = Object.getOwnPropertyDescriptor(value, key);
47
+ if (!descriptor || !("value" in descriptor)) {
48
+ throw failure(
49
+ "SKILL_PROMOTION_INVALID",
50
+ "trusted transition data must contain only own data fields",
51
+ );
52
+ }
53
+ deepFreeze(descriptor.value, seen);
54
+ }
55
+ return Object.freeze(value);
56
+ }
57
+
58
+ function assertDataRecord(value, allowed, label, { required = allowed } = {}) {
59
+ if (
60
+ !value ||
61
+ typeof value !== "object" ||
62
+ Array.isArray(value) ||
63
+ utilTypes.isProxy(value)
64
+ ) {
65
+ throw failure("SKILL_PROMOTION_INVALID", `${label} must be an object`);
66
+ }
67
+ const keys = Reflect.ownKeys(value);
68
+ if (
69
+ keys.some((key) => typeof key !== "string" || !allowed.has(key)) ||
70
+ [...required].some((key) => !Object.hasOwn(value, key))
71
+ ) {
72
+ throw failure(
73
+ "SKILL_PROMOTION_INVALID",
74
+ `${label} must contain exactly the supported fields`,
75
+ );
76
+ }
77
+ for (const key of keys) {
78
+ const descriptor = Object.getOwnPropertyDescriptor(value, key);
79
+ if (!descriptor || !("value" in descriptor) || !descriptor.enumerable) {
80
+ throw failure(
81
+ "SKILL_PROMOTION_INVALID",
82
+ `${label}.${String(key)} must be an own data field`,
83
+ );
84
+ }
85
+ }
86
+ }
87
+
88
+ function assertDigest(value, label) {
89
+ if (typeof value !== "string" || !DIGEST_PATTERN.test(value)) {
90
+ throw failure(
91
+ "SKILL_PROMOTION_INVALID",
92
+ `${label} must be a lowercase sha256 digest`,
93
+ );
94
+ }
95
+ return value;
96
+ }
97
+
98
+ function receiptDigests(request) {
99
+ return deepFreeze(
100
+ Object.fromEntries(
101
+ SKILL_MUTATION_RECEIPT_KINDS.map((kind) => {
102
+ const envelope = request.receipts[`${kind}Receipt`];
103
+ if (typeof envelope !== "string") {
104
+ throw failure(
105
+ "SKILL_PROMOTION_RECEIPTS_INVALID",
106
+ `active mutation requires the ${kind} receipt envelope`,
107
+ );
108
+ }
109
+ return [kind, digestSkillMutationReceiptEnvelope(envelope)];
110
+ }),
111
+ ),
112
+ );
113
+ }
114
+
115
+ function consumeContextFor(request) {
116
+ return buildSkillMutationConsumeContext({
117
+ tenantId: request.tenantId,
118
+ audience: request.audience,
119
+ operationId: request.operationId,
120
+ operation: request.operation,
121
+ transitionSubjectDigest: request.transitionSubjectDigest,
122
+ skillName: request.skillName,
123
+ targetScope: request.targetScope,
124
+ expectedTargetDigest: request.expectedTargetDigest,
125
+ expectedTargetRevision: request.expectedTargetRevision,
126
+ expiresAt: request.expiresAt,
127
+ nonce: request.nonce,
128
+ });
129
+ }
130
+
131
+ function verifyDirectConsumptionReceipt(value, request) {
132
+ let receipt;
133
+ try {
134
+ receipt = verifySkillMutationConsumptionReceipt(value);
135
+ } catch (cause) {
136
+ throw failure(
137
+ "SKILL_PROMOTION_AUTHORITY_REJECTED",
138
+ "authority returned an invalid direct consumption receipt",
139
+ { cause },
140
+ );
141
+ }
142
+ for (const field of [
143
+ "tenantId",
144
+ "audience",
145
+ "operationId",
146
+ "operation",
147
+ "transitionSubjectDigest",
148
+ "skillName",
149
+ "targetScope",
150
+ "expectedTargetDigest",
151
+ "expectedTargetRevision",
152
+ "expiresAt",
153
+ "nonce",
154
+ "requestDigest",
155
+ ]) {
156
+ if (receipt[field] !== request[field]) {
157
+ throw failure(
158
+ "SKILL_PROMOTION_AUTHORITY_REJECTED",
159
+ `authority consumption receipt is not bound to request ${field}`,
160
+ );
161
+ }
162
+ }
163
+ if (receipt.role !== SKILL_MUTATION_ROLES.PROMOTION_CONTROLLER) {
164
+ throw failure(
165
+ "SKILL_PROMOTION_AUTHORITY_REJECTED",
166
+ "active mutation was not consumed by the promotion-controller principal",
167
+ );
168
+ }
169
+ return receipt;
170
+ }
171
+
172
+ function validateAuthorizationInput(value, expectedOperation) {
173
+ assertDataRecord(value, new Set(["capability", "request"]), "authorization");
174
+ const request = verifySkillMutationRequest(value.request);
175
+ if (request.targetScope !== SKILL_MUTATION_TARGET_SCOPES.ACTIVE) {
176
+ throw failure(
177
+ "SKILL_PROMOTION_SCOPE_REJECTED",
178
+ "promotion and rollback require an active-scope authorization",
179
+ );
180
+ }
181
+ if (request.operation !== expectedOperation) {
182
+ throw failure(
183
+ "SKILL_PROMOTION_OPERATION_REJECTED",
184
+ `authorization operation ${request.operation} cannot perform ${expectedOperation}`,
185
+ );
186
+ }
187
+ receiptDigests(request);
188
+ return { capability: value.capability, request };
189
+ }
190
+
191
+ function assertTransitionSubject(request, expectedDigest) {
192
+ if (request.transitionSubjectDigest !== expectedDigest) {
193
+ throw failure(
194
+ "SKILL_PROMOTION_SUBJECT_MISMATCH",
195
+ "authorization transition subject does not match the exact operation target, dependency lock, and active CAS",
196
+ {
197
+ actualTransitionSubjectDigest: request.transitionSubjectDigest,
198
+ expectedTransitionSubjectDigest: expectedDigest,
199
+ },
200
+ );
201
+ }
202
+ }
203
+
204
+ function issueRegistryTransitionCapability(registry, payload) {
205
+ const capability = Object.freeze(Object.create(null));
206
+ TRANSITION_CAPABILITIES.set(capability, {
207
+ registry,
208
+ payload: deepFreeze(payload),
209
+ status: "issued",
210
+ });
211
+ return capability;
212
+ }
213
+
214
+ /**
215
+ * Registry-only broker. It is exported solely to break the module boundary;
216
+ * callers cannot manufacture a WeakMap-backed capability or replay one.
217
+ */
218
+ export function consumeRegistryTransitionCapability(capability, registry) {
219
+ const state =
220
+ capability && typeof capability === "object"
221
+ ? TRANSITION_CAPABILITIES.get(capability)
222
+ : null;
223
+ if (!state || state.registry !== registry) {
224
+ throw failure(
225
+ "SKILL_PROMOTION_TRANSITION_CAPABILITY_INVALID",
226
+ "registry transition capability is forged or bound to another registry",
227
+ );
228
+ }
229
+ if (state.status !== "issued") {
230
+ throw failure(
231
+ "SKILL_PROMOTION_TRANSITION_CAPABILITY_REPLAYED",
232
+ "registry transition capability has already been consumed",
233
+ );
234
+ }
235
+ state.status = "consumed";
236
+ return state.payload;
237
+ }
238
+
239
+ export class SkillPromotionController {
240
+ #tenantId;
241
+
242
+ #registryIdentity;
243
+
244
+ #readCandidate;
245
+
246
+ #readState;
247
+
248
+ #readRelease;
249
+
250
+ #applyTransition;
251
+
252
+ #consumeAuthority;
253
+
254
+ constructor({ candidateRegistry, releaseRegistry, authority } = {}) {
255
+ if (!candidateRegistry || typeof candidateRegistry.read !== "function") {
256
+ throw failure(
257
+ "SKILL_PROMOTION_CANDIDATE_REGISTRY_REQUIRED",
258
+ "trusted candidate registry is required",
259
+ );
260
+ }
261
+ if (
262
+ !releaseRegistry ||
263
+ typeof releaseRegistry.readState !== "function" ||
264
+ typeof releaseRegistry.readRelease !== "function" ||
265
+ typeof releaseRegistry.applyTransition !== "function"
266
+ ) {
267
+ throw failure(
268
+ "SKILL_PROMOTION_RELEASE_REGISTRY_REQUIRED",
269
+ "trusted release registry facade is required",
270
+ );
271
+ }
272
+ if (
273
+ typeof candidateRegistry.tenantId !== "string" ||
274
+ typeof releaseRegistry.tenantId !== "string" ||
275
+ candidateRegistry.tenantId !== releaseRegistry.tenantId
276
+ ) {
277
+ throw failure(
278
+ "SKILL_PROMOTION_TENANT_MISMATCH",
279
+ "candidate and release registries must capture the same tenant",
280
+ );
281
+ }
282
+ if (
283
+ !(authority instanceof SkillMutationAuthority) ||
284
+ typeof authority.consume !== "function"
285
+ ) {
286
+ throw failure(
287
+ "SKILL_PROMOTION_AUTHORITY_REQUIRED",
288
+ "SkillMutationAuthority.consume is required",
289
+ );
290
+ }
291
+
292
+ this.#tenantId = candidateRegistry.tenantId;
293
+ this.#registryIdentity = releaseRegistry;
294
+ this.#readCandidate = candidateRegistry.read.bind(candidateRegistry);
295
+ this.#readState = releaseRegistry.readState.bind(releaseRegistry);
296
+ this.#readRelease = releaseRegistry.readRelease.bind(releaseRegistry);
297
+ this.#applyTransition =
298
+ releaseRegistry.applyTransition.bind(releaseRegistry);
299
+ this.#consumeAuthority = authority.consume.bind(authority);
300
+
301
+ Object.freeze(candidateRegistry);
302
+ Object.freeze(releaseRegistry);
303
+ Object.freeze(authority);
304
+ Object.freeze(this);
305
+ }
306
+
307
+ #currentContentDigest(state) {
308
+ if (state.activeReleaseDigest === null) return EMPTY_SKILL_ACTIVE_DIGEST;
309
+ const active = this.#readRelease(state.activeReleaseDigest);
310
+ if (active.tenantId !== this.#tenantId) {
311
+ throw failure(
312
+ "SKILL_PROMOTION_TENANT_MISMATCH",
313
+ "active release belongs to another tenant",
314
+ );
315
+ }
316
+ return active.contentDigest;
317
+ }
318
+
319
+ #assertRequestCas(request, state, currentContentDigest) {
320
+ if (
321
+ request.skillName !== state.skillName ||
322
+ request.tenantId !== this.#tenantId ||
323
+ state.tenantId !== this.#tenantId ||
324
+ request.expectedTargetRevision !== state.revision ||
325
+ request.expectedTargetDigest !== currentContentDigest
326
+ ) {
327
+ throw failure(
328
+ "SKILL_PROMOTION_CAS_MISMATCH",
329
+ "authorization request is stale for the active Skill state",
330
+ {
331
+ actualRevision: state.revision,
332
+ actualTargetDigest: currentContentDigest,
333
+ },
334
+ );
335
+ }
336
+ }
337
+
338
+ async #consume(capability, request) {
339
+ let directReceipt;
340
+ try {
341
+ directReceipt = await this.#consumeAuthority(
342
+ capability,
343
+ consumeContextFor(request),
344
+ );
345
+ } catch (cause) {
346
+ throw failure(
347
+ "SKILL_PROMOTION_AUTHORITY_REJECTED",
348
+ "SkillMutationAuthority rejected capability consumption",
349
+ { cause },
350
+ );
351
+ }
352
+ return verifyDirectConsumptionReceipt(directReceipt, request);
353
+ }
354
+
355
+ async promote(input = {}) {
356
+ assertDataRecord(
357
+ input,
358
+ new Set(["authorization", "candidateId"]),
359
+ "promotion input",
360
+ );
361
+ const { candidateId, authorization } = input;
362
+ assertDigest(candidateId, "candidateId");
363
+ const { capability, request } = validateAuthorizationInput(
364
+ authorization,
365
+ SKILL_MUTATION_OPERATIONS.PROMOTE,
366
+ );
367
+ const candidate = this.#readCandidate(candidateId);
368
+ if (
369
+ candidate.candidateId !== candidateId ||
370
+ candidate.skillName !== request.skillName ||
371
+ candidate.tenantId !== this.#tenantId ||
372
+ request.tenantId !== this.#tenantId
373
+ ) {
374
+ throw failure(
375
+ "SKILL_PROMOTION_CANDIDATE_MISMATCH",
376
+ "candidate is not bound to the authorized Skill",
377
+ );
378
+ }
379
+ const state = this.#readState(candidate.skillName);
380
+ const currentContentDigest = this.#currentContentDigest(state);
381
+ this.#assertRequestCas(request, state, currentContentDigest);
382
+ const expectedCandidateParent =
383
+ state.activeReleaseDigest === null ? null : currentContentDigest;
384
+ if (candidate.parentDigest !== expectedCandidateParent) {
385
+ throw failure(
386
+ "SKILL_PROMOTION_PARENT_MISMATCH",
387
+ "candidate parent is not the active content digest",
388
+ );
389
+ }
390
+ const dependencyLockDigest = candidate.dependencyLockDigest;
391
+ assertTransitionSubject(
392
+ request,
393
+ digestSkillMutationTransitionSubject({
394
+ tenantId: request.tenantId,
395
+ skillName: request.skillName,
396
+ operation: SKILL_MUTATION_OPERATIONS.PROMOTE,
397
+ candidateId: candidate.candidateId,
398
+ rollbackTargetReleaseDigest: null,
399
+ dependencyLockDigest,
400
+ expectedActiveContentDigest: currentContentDigest,
401
+ expectedActiveRevision: state.revision,
402
+ }),
403
+ );
404
+
405
+ const consumptionReceipt = await this.#consume(capability, request);
406
+ const transitionCapability = issueRegistryTransitionCapability(
407
+ this.#registryIdentity,
408
+ {
409
+ authorityReceipt: consumptionReceipt,
410
+ candidate,
411
+ dependencyLockDigest,
412
+ expectedParentDigest: currentContentDigest,
413
+ expectedRevision: state.revision,
414
+ mutationRequest: request,
415
+ operation: "promote",
416
+ operationId: request.operationId,
417
+ receiptDigests: receiptDigests(request),
418
+ requestDigest: request.requestDigest,
419
+ transitionSubjectDigest: request.transitionSubjectDigest,
420
+ skillName: request.skillName,
421
+ targetReleaseDigest: null,
422
+ tenantId: this.#tenantId,
423
+ },
424
+ );
425
+ return this.#applyTransition(transitionCapability);
426
+ }
427
+
428
+ async rollback(input = {}) {
429
+ assertDataRecord(
430
+ input,
431
+ new Set(["authorization", "targetReleaseDigest"]),
432
+ "rollback input",
433
+ { required: new Set(["authorization"]) },
434
+ );
435
+ const { authorization, targetReleaseDigest = null } = input;
436
+ const { capability, request } = validateAuthorizationInput(
437
+ authorization,
438
+ SKILL_MUTATION_OPERATIONS.ROLLBACK,
439
+ );
440
+ const state = this.#readState(request.skillName);
441
+ const currentContentDigest = this.#currentContentDigest(state);
442
+ this.#assertRequestCas(request, state, currentContentDigest);
443
+ if (state.activeReleaseDigest === null) {
444
+ throw failure(
445
+ "SKILL_PROMOTION_ROLLBACK_REJECTED",
446
+ "a Skill without an active release cannot be rolled back",
447
+ );
448
+ }
449
+ const selected = targetReleaseDigest ?? state.lastKnownGoodReleaseDigest;
450
+ assertDigest(selected, "targetReleaseDigest");
451
+ if (selected !== state.lastKnownGoodReleaseDigest) {
452
+ throw failure(
453
+ "SKILL_PROMOTION_ROLLBACK_REJECTED",
454
+ "rollback target must be the last-known-good release",
455
+ );
456
+ }
457
+ const target = this.#readRelease(selected);
458
+ if (
459
+ request.tenantId !== this.#tenantId ||
460
+ target.tenantId !== this.#tenantId ||
461
+ target.skillName !== request.skillName
462
+ ) {
463
+ throw failure(
464
+ "SKILL_PROMOTION_ROLLBACK_REJECTED",
465
+ "rollback target belongs to another Skill",
466
+ );
467
+ }
468
+ assertTransitionSubject(
469
+ request,
470
+ digestSkillMutationTransitionSubject({
471
+ tenantId: request.tenantId,
472
+ skillName: request.skillName,
473
+ operation: SKILL_MUTATION_OPERATIONS.ROLLBACK,
474
+ candidateId: null,
475
+ rollbackTargetReleaseDigest: target.releaseDigest,
476
+ dependencyLockDigest: target.dependencyLockDigest,
477
+ expectedActiveContentDigest: currentContentDigest,
478
+ expectedActiveRevision: state.revision,
479
+ }),
480
+ );
481
+
482
+ const consumptionReceipt = await this.#consume(capability, request);
483
+ const transitionCapability = issueRegistryTransitionCapability(
484
+ this.#registryIdentity,
485
+ {
486
+ authorityReceipt: consumptionReceipt,
487
+ candidate: null,
488
+ dependencyLockDigest: target.dependencyLockDigest,
489
+ expectedParentDigest: currentContentDigest,
490
+ expectedRevision: state.revision,
491
+ mutationRequest: request,
492
+ operation: "rollback",
493
+ operationId: request.operationId,
494
+ receiptDigests: receiptDigests(request),
495
+ requestDigest: request.requestDigest,
496
+ transitionSubjectDigest: request.transitionSubjectDigest,
497
+ skillName: request.skillName,
498
+ targetReleaseDigest: selected,
499
+ tenantId: this.#tenantId,
500
+ },
501
+ );
502
+ return this.#applyTransition(transitionCapability);
503
+ }
504
+ }
505
+
506
+ Object.freeze(SkillPromotionController.prototype);