chainlesschain 0.166.17 → 0.166.18

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (210) hide show
  1. package/README.md +14 -7
  2. package/package.json +1 -1
  3. package/src/assets/web-panel/assets/{AIOps-CVW2n2rh.js → AIOps-CQSBl3hY.js} +1 -1
  4. package/src/assets/web-panel/assets/{ActionButton-DtuM163O.js → ActionButton-IQt-vRLt.js} +1 -1
  5. package/src/assets/web-panel/assets/{Analytics-BXvl_qHu.js → Analytics-DkHC8O7k.js} +3 -3
  6. package/src/assets/web-panel/assets/{AppLayout-VDBTFKOp.js → AppLayout-CSti2P2T.js} +3 -3
  7. package/src/assets/web-panel/assets/{Artifacts-BNz2xCiG.js → Artifacts-C57hxn3v.js} +1 -1
  8. package/src/assets/web-panel/assets/{Audit-DTu9Hn9P.js → Audit-DByLntcf.js} +1 -1
  9. package/src/assets/web-panel/assets/{BackgroundAgents-D2OCPzLe.js → BackgroundAgents-D1Bd2EOa.js} +1 -1
  10. package/src/assets/web-panel/assets/{Backup-C9M-vG7G.js → Backup-VstHzHng.js} +1 -1
  11. package/src/assets/web-panel/assets/{BaseInput-BUkCNiUQ.js → BaseInput-CLhdexIf.js} +1 -1
  12. package/src/assets/web-panel/assets/{Chat-OJ2ajF7l.js → Chat-BefC_JZS.js} +6 -6
  13. package/src/assets/web-panel/assets/{ChatBubbleRenderer-BdZt76Ab.js → ChatBubbleRenderer-DbXMvgmn.js} +1 -1
  14. package/src/assets/web-panel/assets/{Checkbox-C1gXKDsc.js → Checkbox-CEY982vx.js} +1 -1
  15. package/src/assets/web-panel/assets/{Codegen-BXbznLg7.js → Codegen-Dcm_DTvJ.js} +1 -1
  16. package/src/assets/web-panel/assets/{Col-CbCq5N0i.js → Col-C8SSu4KA.js} +1 -1
  17. package/src/assets/web-panel/assets/{Community-V8Ks6Yun.js → Community-BVV9vwRR.js} +1 -1
  18. package/src/assets/web-panel/assets/{Compact-DkeHQTpQ.js → Compact-B7c5Vo18.js} +1 -1
  19. package/src/assets/web-panel/assets/{Compliance-JeLKwn4Q.js → Compliance-Tq9jSZ5a.js} +1 -1
  20. package/src/assets/web-panel/assets/{Cowork-azfetcBz.js → Cowork-BJUjT09Q.js} +3 -3
  21. package/src/assets/web-panel/assets/{Cron-Q691i_Gp.js → Cron-CjV9PEaS.js} +2 -2
  22. package/src/assets/web-panel/assets/{Crosschain-BLjDRbr_.js → Crosschain-Dl7ReL8F.js} +1 -1
  23. package/src/assets/web-panel/assets/{DID-D8ptXplK.js → DID-CG8PjWmM.js} +2 -2
  24. package/src/assets/web-panel/assets/{Dashboard-Ctj2VVZL.js → Dashboard-CDBM6wmF.js} +2 -2
  25. package/src/assets/web-panel/assets/{Dropdown-D8VgkMJ8.js → Dropdown-8_55bXTD.js} +1 -1
  26. package/src/assets/web-panel/assets/{EmailListRenderer-CQPe5S6q.js → EmailListRenderer-D3qOU1Cm.js} +1 -1
  27. package/src/assets/web-panel/assets/{FamilyGuardDashboard-ySA-7SiN.js → FamilyGuardDashboard-Bv5oWMZF.js} +1 -1
  28. package/src/assets/web-panel/assets/{Federation-1PMD-fBf.js → Federation-hpZ1xkKd.js} +1 -1
  29. package/src/assets/web-panel/assets/{FormItemContext-C_jr6U4Q.js → FormItemContext-BN7GT191.js} +1 -1
  30. package/src/assets/web-panel/assets/{GenericCardRenderer-Bu3bKf2B.js → GenericCardRenderer-PCRhfA7M.js} +1 -1
  31. package/src/assets/web-panel/assets/{Git-Dv4Fa_T5.js → Git-CIlP2vy7.js} +2 -2
  32. package/src/assets/web-panel/assets/{Governance-DOEy1n1k.js → Governance-81lgm7YL.js} +1 -1
  33. package/src/assets/web-panel/assets/{Inference-DkvqzWcJ.js → Inference-Cs24xMOV.js} +1 -1
  34. package/src/assets/web-panel/assets/{KnowledgeGraph-DdUJK949.js → KnowledgeGraph-CrJLVCZo.js} +1 -1
  35. package/src/assets/web-panel/assets/{Logs-CRelcdUZ.js → Logs-DLyO8RKT.js} +2 -2
  36. package/src/assets/web-panel/assets/{MarkdownRenderer-0AHbbTA4.js → MarkdownRenderer-DBOqijrP.js} +1 -1
  37. package/src/assets/web-panel/assets/{Marketplace-BuuBKsev.js → Marketplace-Dz7AbQZc.js} +1 -1
  38. package/src/assets/web-panel/assets/{McpTools-DlgpEmA1.js → McpTools-BM0aFiWv.js} +3 -3
  39. package/src/assets/web-panel/assets/{Memory-DTfdqUGy.js → Memory-C9SJzENX.js} +2 -2
  40. package/src/assets/web-panel/assets/{MobileBridge-CXLE5ZFO.js → MobileBridge-BO2HnNRk.js} +1 -1
  41. package/src/assets/web-panel/assets/{MobileProjects-Dz25OB0n.js → MobileProjects-CK_jMz0l.js} +1 -1
  42. package/src/assets/web-panel/assets/{Mtc-kEL0fhgD.js → Mtc-CfhrJsdO.js} +2 -2
  43. package/src/assets/web-panel/assets/{MtcAudit-BE4UvFJU.js → MtcAudit-BE2ELxgI.js} +4 -4
  44. package/src/assets/web-panel/assets/{Multisig-CSApSn8c.js → Multisig-CgR_E3Jd.js} +3 -3
  45. package/src/assets/web-panel/assets/{NLProgramming-D11FpKWx.js → NLProgramming-Cw9445_K.js} +1 -1
  46. package/src/assets/web-panel/assets/{Notes-CDL-D2CS.js → Notes-DZimkTrb.js} +3 -3
  47. package/src/assets/web-panel/assets/{NotificationSettings-C0Q6lO-c.js → NotificationSettings-DxHVh4uT.js} +1 -1
  48. package/src/assets/web-panel/assets/OrderTableRenderer-Iy-pnX56.js +1 -0
  49. package/src/assets/web-panel/assets/{Organization-Dc-maVAq.js → Organization-pPXJtWo4.js} +4 -4
  50. package/src/assets/web-panel/assets/{Overflow-B5mUpSvi.js → Overflow-YamdTJwA.js} +1 -1
  51. package/src/assets/web-panel/assets/{P2P-TjXV53wq.js → P2P-BvqP53x7.js} +2 -2
  52. package/src/assets/web-panel/assets/PdhVaultBrowser-Blz1Xn2n.js +7 -0
  53. package/src/assets/web-panel/assets/{Permissions-DKRzQ2O6.js → Permissions-CgFRQqFJ.js} +4 -4
  54. package/src/assets/web-panel/assets/{PersonalDataHub-Dx2eqft8.js → PersonalDataHub-CFeo3-6O.js} +4 -4
  55. package/src/assets/web-panel/assets/{Pipeline-CnYZMB5p.js → Pipeline-BkaRojEK.js} +1 -1
  56. package/src/assets/web-panel/assets/{Privacy-BonKpPnp.js → Privacy-BNZEx2Qp.js} +1 -1
  57. package/src/assets/web-panel/assets/{ProjectInit-DWO0Du3J.js → ProjectInit-HUBOgEEK.js} +2 -2
  58. package/src/assets/web-panel/assets/{ProjectSettings-C9jqyYRv.js → ProjectSettings-CFMmhROS.js} +2 -2
  59. package/src/assets/web-panel/assets/{Projects-CYpKwXbz.js → Projects-Dc517BXp.js} +1 -1
  60. package/src/assets/web-panel/assets/{Providers-CW-fkwJq.js → Providers-DTLf8bgj.js} +1 -1
  61. package/src/assets/web-panel/assets/{QrScannerModal-Cw0J3Zud.js → QrScannerModal-BBNjPszk.js} +1 -1
  62. package/src/assets/web-panel/assets/{QuickAsk-BH4A4IpR.js → QuickAsk-DCsHvk3G.js} +1 -1
  63. package/src/assets/web-panel/assets/{Recommend-CDwMwJus.js → Recommend-Dpr5YSop.js} +1 -1
  64. package/src/assets/web-panel/assets/{RemoteSession-DKIPwzTf.js → RemoteSession-DmKnvqGg.js} +2 -2
  65. package/src/assets/web-panel/assets/{Reputation-CklDj7qS.js → Reputation-CSRfuurX.js} +1 -1
  66. package/src/assets/web-panel/assets/{Row-DJWZKsH0.js → Row-CDj3pQ_w.js} +1 -1
  67. package/src/assets/web-panel/assets/{RssFeed-BfSLCewo.js → RssFeed-DI0a_kNq.js} +2 -2
  68. package/src/assets/web-panel/assets/{Search-B0IST--x.js → Search-jAy7B2nU.js} +1 -1
  69. package/src/assets/web-panel/assets/{Security-h1sYNWtt.js → Security-CkHTqYhc.js} +4 -4
  70. package/src/assets/web-panel/assets/{Services-Gh0-oxI_.js → Services-CP-0Nzga.js} +2 -2
  71. package/src/assets/web-panel/assets/{Skeleton-CNDV_jOs.js → Skeleton-BkF5-p3n.js} +1 -1
  72. package/src/assets/web-panel/assets/{Skills-ByCLTh1n.js → Skills-BDu-utDE.js} +1 -1
  73. package/src/assets/web-panel/assets/{Sla-BfyrJTJz.js → Sla-CQvq_2Qb.js} +1 -1
  74. package/src/assets/web-panel/assets/{SpeechSettings-BQzo9kNX.js → SpeechSettings-T7Ap512g.js} +1 -1
  75. package/src/assets/web-panel/assets/{SyncSettings-B6fS6z_1.js → SyncSettings-Bt-PLvot.js} +2 -2
  76. package/src/assets/web-panel/assets/{Tasks-C-uKCP-l.js → Tasks-yLEcCogg.js} +1 -1
  77. package/src/assets/web-panel/assets/{Templates-D6oDy7wL.js → Templates-ByN3fmbr.js} +1 -1
  78. package/src/assets/web-panel/assets/{Tenant-D3LT2wNQ.js → Tenant-DBI7hITH.js} +1 -1
  79. package/src/assets/web-panel/assets/{Terminal-BteLcv_O.js → Terminal-B77_DlGX.js} +2 -2
  80. package/src/assets/web-panel/assets/{TimelineRenderer-DHHuxfqH.js → TimelineRenderer-CsLTB_QO.js} +1 -1
  81. package/src/assets/web-panel/assets/{Tokens-BRQeykXw.js → Tokens-BV2N6cR8.js} +1 -1
  82. package/src/assets/web-panel/assets/{Trigger-p2XaFWEu.js → Trigger-CjXWGH3p.js} +1 -1
  83. package/src/assets/web-panel/assets/{Trust-BxadBMft.js → Trust-uqXIq3WC.js} +1 -1
  84. package/src/assets/web-panel/assets/{UkeySign-DRBP7LDk.js → UkeySign-D-6qu4vY.js} +1 -1
  85. package/src/assets/web-panel/assets/{VideoEditing-CZ3NZkh7.js → VideoEditing-CxGmx_2e.js} +1 -1
  86. package/src/assets/web-panel/assets/{Wallet-C3NTBN7G.js → Wallet-DSq7WlAg.js} +4 -4
  87. package/src/assets/web-panel/assets/{WebAuthn-C7LVgMlb.js → WebAuthn-CDBiD57O.js} +2 -2
  88. package/src/assets/web-panel/assets/{WorkflowEditor-0_Iveog7.js → WorkflowEditor-Z8f63TSb.js} +1 -1
  89. package/src/assets/web-panel/assets/{chat-DzzluYo3.js → chat-B-UZFBjy.js} +1 -1
  90. package/src/assets/web-panel/assets/{colors-DSMYXpaj.js → colors-CFk7K-7Q.js} +1 -1
  91. package/src/assets/web-panel/assets/{compact-item-W76u1VSV.js → compact-item-X0IVMvgi.js} +1 -1
  92. package/src/assets/web-panel/assets/{createContext-otsESjb5.js → createContext-DWg4j2n6.js} +1 -1
  93. package/src/assets/web-panel/assets/devWarning-B2IW1J_J.js +1 -0
  94. package/src/assets/web-panel/assets/{hasIn-3sNLrvug.js → hasIn-CkVCwNNF.js} +1 -1
  95. package/src/assets/web-panel/assets/{index-BdtmEKat.js → index-5OcZHdzc.js} +1 -1
  96. package/src/assets/web-panel/assets/{index-DS9WYDMf.js → index-B273IY-E.js} +1 -1
  97. package/src/assets/web-panel/assets/{index-NaO9AE-F.js → index-B3-B3FWD.js} +1 -1
  98. package/src/assets/web-panel/assets/{index-BcGJtmHM.js → index-BB4KHHcv.js} +1 -1
  99. package/src/assets/web-panel/assets/index-BCp2ir-1.js +1 -0
  100. package/src/assets/web-panel/assets/{index-pVm_iUre.js → index-BG-GaZri.js} +1 -1
  101. package/src/assets/web-panel/assets/{index-BpcQYzP7.js → index-BPBia4b-.js} +1 -1
  102. package/src/assets/web-panel/assets/{index-BXLx9HfA.js → index-BipwEbTA.js} +1 -1
  103. package/src/assets/web-panel/assets/{index-uSCkbpZH.js → index-BoskgPt9.js} +1 -1
  104. package/src/assets/web-panel/assets/{index-DMmdIpLA.js → index-Btqk6zWb.js} +1 -1
  105. package/src/assets/web-panel/assets/index-Bw4iuzRD.js +1 -0
  106. package/src/assets/web-panel/assets/{index-DLDh1OW3.js → index-BzXjYUmd.js} +1 -1
  107. package/src/assets/web-panel/assets/{index-B9X_jqXe.js → index-C1ZAOJRW.js} +1 -1
  108. package/src/assets/web-panel/assets/{index-vBKPcmAP.js → index-CCLwoATF.js} +1 -1
  109. package/src/assets/web-panel/assets/{index-vUmYkqmu.js → index-CDfSD07S.js} +1 -1
  110. package/src/assets/web-panel/assets/{index-D7A5U2IH.js → index-CLlP1E4X.js} +1 -1
  111. package/src/assets/web-panel/assets/{index-ClOFeZ-t.js → index-CMCEXBRh.js} +1 -1
  112. package/src/assets/web-panel/assets/{index-Cg86EzNp.js → index-CN8HZlLH.js} +1 -1
  113. package/src/assets/web-panel/assets/{index-Ddd4mird.js → index-CNKFsBlz.js} +1 -1
  114. package/src/assets/web-panel/assets/{index-CE7tSow-.js → index-CUBkvFPH.js} +1 -1
  115. package/src/assets/web-panel/assets/{index-GlIOE4ja.js → index-Ca0mij1n.js} +1 -1
  116. package/src/assets/web-panel/assets/{index-DdU7IkKX.js → index-CbIAWbma.js} +1 -1
  117. package/src/assets/web-panel/assets/{index-UUHCwfNq.js → index-CbihMThv.js} +1 -1
  118. package/src/assets/web-panel/assets/{index-BRsD1PT2.js → index-CvOa-IFV.js} +1 -1
  119. package/src/assets/web-panel/assets/{index-C2aht9-M.js → index-Cvwu_rA_.js} +1 -1
  120. package/src/assets/web-panel/assets/{index-BJsQio5O.js → index-D-EJNxgR.js} +1 -1
  121. package/src/assets/web-panel/assets/{index-q7ftLqNt.js → index-D8uEL8Uw.js} +1 -1
  122. package/src/assets/web-panel/assets/{index-B1I1AYJs.js → index-D92g4l3n.js} +1 -1
  123. package/src/assets/web-panel/assets/{index-DZKAzEge.js → index-DCqpl_E_.js} +1 -1
  124. package/src/assets/web-panel/assets/{index-CKoFyHYQ.js → index-DLSBks8h.js} +1 -1
  125. package/src/assets/web-panel/assets/{index-BDlDO2px.js → index-Db_5xhrU.js} +1 -1
  126. package/src/assets/web-panel/assets/{index-CENWwjef.js → index-DcDggpWR.js} +1 -1
  127. package/src/assets/web-panel/assets/{index-DH4cl52p.js → index-DeWKPQGs.js} +1 -1
  128. package/src/assets/web-panel/assets/{index-DTA8Iavv.js → index-DhVHoqGL.js} +3 -3
  129. package/src/assets/web-panel/assets/{index-DmpaC2yk.js → index-DyWlOuak.js} +1 -1
  130. package/src/assets/web-panel/assets/{index-BA8mZxd-.js → index-LU54bChK.js} +1 -1
  131. package/src/assets/web-panel/assets/{index-CtFOt5x1.js → index-OjnAwUJo.js} +1 -1
  132. package/src/assets/web-panel/assets/{index-B5LOYdYm.js → index-P83LDnCz.js} +1 -1
  133. package/src/assets/web-panel/assets/{index-MhQQ3h8q.js → index-_-9k3Tna.js} +1 -1
  134. package/src/assets/web-panel/assets/{initDefaultProps-DF6llUXV.js → initDefaultProps-CFNgY8lJ.js} +1 -1
  135. package/src/assets/web-panel/assets/{motion-vtDV8_5K.js → motion-D-5RrDzn.js} +1 -1
  136. package/src/assets/web-panel/assets/{move-B6c0bdy2.js → move-DN-L0Bd-.js} +1 -1
  137. package/src/assets/web-panel/assets/{mtc-parser-DlLYWuBK.js → mtc-parser-6mgLSeCK.js} +1 -1
  138. package/src/assets/web-panel/assets/{omit-BH0zOxLN.js → omit-ClWW8eTP.js} +1 -1
  139. package/src/assets/web-panel/assets/{pickAttrs-D9di8_In.js → pickAttrs-Vystq3R8.js} +1 -1
  140. package/src/assets/web-panel/assets/{placementArrow-BDSngufM.js → placementArrow-D0H9hK4W.js} +1 -1
  141. package/src/assets/web-panel/assets/{responsiveObserve-C_EawMvP.js → responsiveObserve-B3iNRU-a.js} +1 -1
  142. package/src/assets/web-panel/assets/{slide-B_4wShxA.js → slide-CiMXe6am.js} +1 -1
  143. package/src/assets/web-panel/assets/{statusUtils-CbQu6qJ-.js → statusUtils-CGDZJ1uT.js} +1 -1
  144. package/src/assets/web-panel/assets/{styleChecker-CvXJWYXb.js → styleChecker-D2zA6NkY.js} +1 -1
  145. package/src/assets/web-panel/assets/{useFlexGapSupport-jjY1XknO.js → useFlexGapSupport-C1sYU6QE.js} +1 -1
  146. package/src/assets/web-panel/assets/{useFs-CMXBdsK_.js → useFs-BzUHSjg2.js} +1 -1
  147. package/src/assets/web-panel/assets/{usePersonalDataHub-YbAhhPXx.js → usePersonalDataHub-CxB5yZCq.js} +1 -1
  148. package/src/assets/web-panel/assets/{vnode-BFCTKc0H.js → vnode-BfUfMwtc.js} +1 -1
  149. package/src/assets/web-panel/assets/{zoom-CY9aBl5O.js → zoom-CFYmj1TO.js} +1 -1
  150. package/src/assets/web-panel/index.html +1 -1
  151. package/src/command-help-index.json +1 -1
  152. package/src/commands/agent.js +80 -2
  153. package/src/commands/evolution.js +33 -27
  154. package/src/commands/serve.js +11 -2
  155. package/src/data/changelog.json +5 -5
  156. package/src/gateways/ws/session-protocol.js +1 -0
  157. package/src/gateways/ws/ws-agent-handler.js +70 -2
  158. package/src/gateways/ws/ws-server.js +8 -0
  159. package/src/lib/app-server/cli-agent-kernel-adapter.js +4 -0
  160. package/src/lib/app-server/graph-runtime.js +115 -8
  161. package/src/lib/app-server/server.js +21 -2
  162. package/src/lib/app-server/websocket-transport.js +2 -0
  163. package/src/lib/evolution/agent-evolution-ingress.js +300 -0
  164. package/src/lib/evolution/agent-evolution-runtime-composition-brand.js +22 -0
  165. package/src/lib/evolution/agent-evolution-runtime-composition.js +369 -0
  166. package/src/lib/evolution/evidence-backed-wiki-maintainer.js +752 -0
  167. package/src/lib/evolution/evolution-artifact-ports.js +48 -0
  168. package/src/lib/evolution/evolution-eval-gate.js +11 -4
  169. package/src/lib/evolution/evolution-evidence-artifact-adapter.js +499 -0
  170. package/src/lib/evolution/evolution-evidence-projector.js +61 -1
  171. package/src/lib/evolution/evolution-file-witness.js +601 -0
  172. package/src/lib/evolution/evolution-ledger-file-backend.js +176 -0
  173. package/src/lib/evolution/evolution-ledger-ports.js +353 -16
  174. package/src/lib/evolution/evolution-ledger.js +1355 -54
  175. package/src/lib/evolution/evolution-run-ledger-adapter.js +409 -0
  176. package/src/lib/evolution/evolution-run-wiki-maintenance-source.js +300 -0
  177. package/src/lib/evolution/scheduled-batch-wiki-maintenance-source.js +368 -0
  178. package/src/lib/evolution/skill-candidate-registry.js +649 -0
  179. package/src/lib/evolution/skill-promotion-controller.js +109 -38
  180. package/src/lib/evolution/skill-promotion-review-ledger-adapter.js +451 -0
  181. package/src/lib/evolution/skill-promotion-review.js +793 -0
  182. package/src/lib/evolution/skill-registry-transition-capability.js +55 -0
  183. package/src/lib/evolution/skill-registry-transition-ledger-adapter.js +1280 -0
  184. package/src/lib/evolution/skill-registry-transition-source.js +372 -0
  185. package/src/lib/evolution/skill-release-registry.js +2191 -11
  186. package/src/lib/evolution/skill-target-matrix-eval.js +133 -10
  187. package/src/lib/evolution/structured-memory-agent-control-plane.js +222 -0
  188. package/src/lib/evolution/structured-memory-authority-ledger-adapter.js +351 -0
  189. package/src/lib/evolution/structured-memory-ledger-adapter.js +220 -0
  190. package/src/lib/evolution/structured-memory-policy-receipt-writer.js +164 -0
  191. package/src/lib/evolution/structured-memory-post-compact-hook.js +87 -0
  192. package/src/lib/evolution/structured-memory-promotion-receipt-writer.js +221 -0
  193. package/src/lib/evolution/structured-memory-semantic-review-pipeline.js +313 -0
  194. package/src/lib/evolution/wiki-maintainer-ledger-adapter.js +200 -0
  195. package/src/lib/evolution/wiki-maintenance-trigger-ledger-adapter.js +687 -0
  196. package/src/lib/evolution-system.js +65 -30
  197. package/src/lib/mcp-lifecycle-authority.js +21 -2
  198. package/src/lib/remote-approval-bridge.js +24 -0
  199. package/src/lib/scheduler-kernel/store.js +62 -14
  200. package/src/repl/agent-repl.js +45 -0
  201. package/src/runtime/agent-runtime.js +114 -1
  202. package/src/runtime/headless-runner.js +19 -0
  203. package/src/runtime/headless-stream.js +43 -0
  204. package/src/runtime/runtime-factory.js +19 -1
  205. package/src/assets/web-panel/assets/OrderTableRenderer-ZRE0OKbj.js +0 -1
  206. package/src/assets/web-panel/assets/PdhVaultBrowser-DvV2btIr.js +0 -7
  207. package/src/assets/web-panel/assets/devWarning-BztaXF84.js +0 -1
  208. package/src/assets/web-panel/assets/index-4jP_hG_4.js +0 -1
  209. package/src/assets/web-panel/assets/index-zxwuAXL5.js +0 -1
  210. package/src/lib/learning/learning-hooks.js +0 -123
@@ -0,0 +1,752 @@
1
+ import { createHash } from "node:crypto";
2
+
3
+ export const WIKI_STATE_SCHEMA = "chainlesschain.evolution-wiki-state/v1";
4
+ export const WIKI_EVIDENCE_SCHEMA = "chainlesschain.evolution-wiki-evidence/v1";
5
+ export const WIKI_REVISION_SCHEMA = "chainlesschain.evolution-wiki-revision/v1";
6
+ export const WIKI_MAINTENANCE_REQUEST_SCHEMA =
7
+ "chainlesschain.evolution-wiki-maintenance-request/v1";
8
+
9
+ export const WIKI_PATTERN_STATUS = Object.freeze({
10
+ HYPOTHESIS: "hypothesis",
11
+ CORROBORATED: "corroborated",
12
+ CONTRADICTED: "contradicted",
13
+ STALE: "stale",
14
+ REVOKED: "revoked",
15
+ TOMBSTONED: "tombstoned",
16
+ });
17
+
18
+ const DIGEST = /^sha256:[a-f0-9]{64}$/u;
19
+ const PATTERN_ID = /^pat-[a-z0-9][a-z0-9-]{2,127}$/u;
20
+ const MAINTENANCE_REQUEST_ID = /^wiki-maintenance:[a-f0-9]{64}$/u;
21
+ const REVISION_ID = /^wiki:[a-f0-9]{64}$/u;
22
+ const KINDS = new Set(["success", "failure", "constraint", "anti-pattern"]);
23
+ const EVIDENCE_KINDS = new Set([
24
+ "user-statement",
25
+ "tool-observation",
26
+ "model-inference",
27
+ "grader-receipt",
28
+ "proposal-decision",
29
+ "tombstone",
30
+ ]);
31
+ const EVIDENCE_FIELDS = new Set([
32
+ "schema",
33
+ "tenantId",
34
+ "ref",
35
+ "sourceDigest",
36
+ "projectionDigest",
37
+ "artifactRef",
38
+ "trustedProjection",
39
+ "trustDomain",
40
+ "kind",
41
+ "status",
42
+ "observedAt",
43
+ "expiresAt",
44
+ "data",
45
+ "envelopeDigest",
46
+ ]);
47
+ const OPERATIONS = new Set([
48
+ "upsert",
49
+ "merge",
50
+ "revoke",
51
+ "tombstone",
52
+ "proposal-impact",
53
+ ]);
54
+ const FORBIDDEN_KEYS = new Set([
55
+ "content",
56
+ "output",
57
+ "payload",
58
+ "prompt",
59
+ "secret",
60
+ "token",
61
+ ]);
62
+
63
+ function canonical(value) {
64
+ if (value === null || typeof value !== "object") return JSON.stringify(value);
65
+ if (Array.isArray(value)) return `[${value.map(canonical).join(",")}]`;
66
+ return `{${Object.keys(value)
67
+ .sort()
68
+ .map((key) => `${JSON.stringify(key)}:${canonical(value[key])}`)
69
+ .join(",")}}`;
70
+ }
71
+
72
+ function hash(value) {
73
+ return `sha256:${createHash("sha256").update(canonical(value)).digest("hex")}`;
74
+ }
75
+
76
+ function clone(value) {
77
+ return value == null ? value : structuredClone(value);
78
+ }
79
+
80
+ function freeze(value) {
81
+ if (value && typeof value === "object" && !Object.isFrozen(value)) {
82
+ Object.freeze(value);
83
+ for (const child of Object.values(value)) freeze(child);
84
+ }
85
+ return value;
86
+ }
87
+
88
+ function requiredString(value, name) {
89
+ if (typeof value !== "string" || value.trim() === "")
90
+ throw new TypeError(`${name} is required`);
91
+ return value;
92
+ }
93
+
94
+ function stringList(value, name, { allowEmpty = true, max = 128 } = {}) {
95
+ if (
96
+ !Array.isArray(value) ||
97
+ (!allowEmpty && value.length === 0) ||
98
+ value.length > max ||
99
+ value.some((item) => typeof item !== "string" || item.trim() === "")
100
+ ) {
101
+ throw new TypeError(`${name} must be a bounded string list`);
102
+ }
103
+ return [...new Set(value)].sort();
104
+ }
105
+
106
+ function validTime(value, name) {
107
+ requiredString(value, name);
108
+ if (!Number.isFinite(Date.parse(value)))
109
+ throw new TypeError(`${name} must be an ISO timestamp`);
110
+ return value;
111
+ }
112
+
113
+ function assertMetadataOnly(value, path = "data") {
114
+ if (!value || typeof value !== "object") return;
115
+ for (const [key, child] of Object.entries(value)) {
116
+ if (FORBIDDEN_KEYS.has(key.toLowerCase())) {
117
+ const error = new Error(
118
+ `${path}.${key} cannot contain raw or secret material`,
119
+ );
120
+ error.code = "WIKI_MAINTAINER_UNSAFE_EVIDENCE";
121
+ throw error;
122
+ }
123
+ assertMetadataOnly(child, `${path}.${key}`);
124
+ }
125
+ }
126
+
127
+ export function createEmptyWikiState(tenantId) {
128
+ return freeze({
129
+ schema: WIKI_STATE_SCHEMA,
130
+ tenantId: requiredString(tenantId, "tenantId"),
131
+ revision: 0,
132
+ revisionId: null,
133
+ patterns: {},
134
+ index: [],
135
+ evidence: {},
136
+ evidenceDependents: {},
137
+ maintenanceRequests: {},
138
+ skillImpact: {},
139
+ evolutionLog: [],
140
+ });
141
+ }
142
+
143
+ function normalizeMaintenanceRequest(input, tenantId) {
144
+ if (input == null) return null;
145
+ const keys = Reflect.ownKeys(input);
146
+ if (
147
+ !input ||
148
+ typeof input !== "object" ||
149
+ Array.isArray(input) ||
150
+ keys.length !== 4 ||
151
+ keys.some(
152
+ (key) =>
153
+ typeof key !== "string" ||
154
+ !["schema", "tenantId", "requestId", "requestDigest"].includes(key),
155
+ ) ||
156
+ input.schema !== WIKI_MAINTENANCE_REQUEST_SCHEMA ||
157
+ input.tenantId !== tenantId ||
158
+ !DIGEST.test(input.requestDigest ?? "") ||
159
+ !MAINTENANCE_REQUEST_ID.test(input.requestId ?? "") ||
160
+ input.requestId !==
161
+ `wiki-maintenance:${input.requestDigest.slice("sha256:".length)}`
162
+ ) {
163
+ throw new TypeError(
164
+ "maintenanceRequest must be exact, tenant-scoped, and digest-bound",
165
+ );
166
+ }
167
+ return freeze(clone(input));
168
+ }
169
+
170
+ export function digestWikiState(state) {
171
+ return hash(state);
172
+ }
173
+
174
+ function verifyStateEnvelope(envelope, tenantId) {
175
+ const state = envelope?.state;
176
+ if (
177
+ envelope?.trusted !== true ||
178
+ state?.schema !== WIKI_STATE_SCHEMA ||
179
+ state.tenantId !== tenantId ||
180
+ !DIGEST.test(envelope?.stateDigest ?? "") ||
181
+ envelope.stateDigest !== hash(state)
182
+ ) {
183
+ const error = new Error(
184
+ "Wiki state is not trusted, digest-bound, and tenant-scoped",
185
+ );
186
+ error.code = "WIKI_MAINTAINER_UNTRUSTED_STATE";
187
+ throw error;
188
+ }
189
+ return clone(state);
190
+ }
191
+
192
+ function verifyEvidence(envelope, tenantId, expectedRef) {
193
+ const signed = Object.fromEntries(
194
+ Object.entries(envelope ?? {}).filter(([key]) => key !== "envelopeDigest"),
195
+ );
196
+ if (
197
+ Object.keys(envelope ?? {}).some((key) => !EVIDENCE_FIELDS.has(key)) ||
198
+ envelope?.schema !== WIKI_EVIDENCE_SCHEMA ||
199
+ envelope?.trustedProjection !== true ||
200
+ envelope?.tenantId !== tenantId ||
201
+ envelope?.ref !== expectedRef ||
202
+ !DIGEST.test(envelope?.sourceDigest ?? "") ||
203
+ !DIGEST.test(envelope?.projectionDigest ?? "") ||
204
+ !DIGEST.test(envelope?.envelopeDigest ?? "") ||
205
+ envelope.envelopeDigest !== hash(signed) ||
206
+ !EVIDENCE_KINDS.has(envelope?.kind) ||
207
+ !["active", "revoked", "deleted"].includes(envelope?.status)
208
+ ) {
209
+ const error = new Error(
210
+ "evidence is not authenticated, trusted, and tenant-scoped",
211
+ );
212
+ error.code = "WIKI_MAINTAINER_UNTRUSTED_EVIDENCE";
213
+ throw error;
214
+ }
215
+ requiredString(envelope.artifactRef, "evidence.artifactRef");
216
+ requiredString(envelope.trustDomain, "evidence.trustDomain");
217
+ validTime(envelope.observedAt, "evidence.observedAt");
218
+ if (envelope.expiresAt != null)
219
+ validTime(envelope.expiresAt, "evidence.expiresAt");
220
+ assertMetadataOnly(envelope.data);
221
+ return freeze(clone(envelope));
222
+ }
223
+
224
+ function normalizePattern(input) {
225
+ const patternId = requiredString(input?.patternId, "pattern.patternId");
226
+ if (!PATTERN_ID.test(patternId))
227
+ throw new TypeError("patternId must be canonical");
228
+ if (!KINDS.has(input.kind)) throw new TypeError("pattern.kind is invalid");
229
+ const confidence = Number(input.confidence);
230
+ if (!Number.isFinite(confidence) || confidence < 0 || confidence > 1) {
231
+ throw new TypeError("pattern.confidence must be between zero and one");
232
+ }
233
+ return {
234
+ patternId,
235
+ kind: input.kind,
236
+ summary: requiredString(input.summary, "pattern.summary"),
237
+ rootCause: requiredString(input.rootCause, "pattern.rootCause"),
238
+ procedure:
239
+ input.procedure == null
240
+ ? null
241
+ : requiredString(input.procedure, "pattern.procedure"),
242
+ appliesWhen: stringList(input.appliesWhen, "pattern.appliesWhen"),
243
+ doesNotApplyWhen: stringList(
244
+ input.doesNotApplyWhen,
245
+ "pattern.doesNotApplyWhen",
246
+ ),
247
+ positiveEvidence: stringList(
248
+ input.positiveEvidence,
249
+ "pattern.positiveEvidence",
250
+ { allowEmpty: false },
251
+ ),
252
+ negativeEvidence: stringList(
253
+ input.negativeEvidence ?? [],
254
+ "pattern.negativeEvidence",
255
+ ),
256
+ contradicts: stringList(input.contradicts ?? [], "pattern.contradicts"),
257
+ supersedes: stringList(input.supersedes ?? [], "pattern.supersedes"),
258
+ confidence,
259
+ trustDomains: stringList(input.trustDomains ?? [], "pattern.trustDomains"),
260
+ lastVerifiedAt:
261
+ input.lastVerifiedAt == null
262
+ ? null
263
+ : validTime(input.lastVerifiedAt, "pattern.lastVerifiedAt"),
264
+ expiresAt:
265
+ input.expiresAt == null
266
+ ? null
267
+ : validTime(input.expiresAt, "pattern.expiresAt"),
268
+ skillNames: stringList(input.skillNames ?? [], "pattern.skillNames"),
269
+ };
270
+ }
271
+
272
+ function fingerprint(pattern) {
273
+ return hash({
274
+ kind: pattern.kind,
275
+ summary: pattern.summary,
276
+ rootCause: pattern.rootCause,
277
+ appliesWhen: pattern.appliesWhen,
278
+ doesNotApplyWhen: pattern.doesNotApplyWhen,
279
+ });
280
+ }
281
+
282
+ function appendLog(state, type, subjectId, effectiveAt, details = {}) {
283
+ const entry = {
284
+ sequence: state.evolutionLog.length + 1,
285
+ type,
286
+ subjectId,
287
+ effectiveAt,
288
+ details,
289
+ };
290
+ entry.entryDigest = hash(entry);
291
+ state.evolutionLog.push(entry);
292
+ }
293
+
294
+ function evidenceUsable(item, effectiveAt) {
295
+ return (
296
+ item?.status === "active" &&
297
+ (item.expiresAt == null ||
298
+ Date.parse(item.expiresAt) > Date.parse(effectiveAt))
299
+ );
300
+ }
301
+
302
+ function rebuildDerivedState(state, effectiveAt, descriptor) {
303
+ const dependents = {};
304
+ for (const pattern of Object.values(state.patterns)) {
305
+ for (const ref of [
306
+ ...pattern.positiveEvidence,
307
+ ...pattern.negativeEvidence,
308
+ ]) {
309
+ (dependents[ref] ??= []).push(pattern.patternId);
310
+ }
311
+ if (
312
+ [WIKI_PATTERN_STATUS.REVOKED, WIKI_PATTERN_STATUS.TOMBSTONED].includes(
313
+ pattern.status,
314
+ )
315
+ )
316
+ continue;
317
+ const positive = pattern.positiveEvidence
318
+ .map((ref) => state.evidence[ref])
319
+ .filter((item) => evidenceUsable(item, effectiveAt));
320
+ const negative = pattern.negativeEvidence
321
+ .map((ref) => state.evidence[ref])
322
+ .filter((item) => evidenceUsable(item, effectiveAt));
323
+ const domains = [
324
+ ...new Set(positive.map((item) => item.trustDomain)),
325
+ ].sort();
326
+ const hasGrader = positive.some((item) => item.kind === "grader-receipt");
327
+ const expired =
328
+ pattern.expiresAt != null &&
329
+ Date.parse(pattern.expiresAt) <= Date.parse(effectiveAt);
330
+ const verifiedAt = pattern.lastVerifiedAt ?? pattern.updatedAt;
331
+ const ageDays = Math.max(
332
+ 0,
333
+ (Date.parse(effectiveAt) - Date.parse(verifiedAt)) / 86_400_000,
334
+ );
335
+ const decayFactor = 0.5 ** (ageDays / descriptor.decayHalfLifeDays);
336
+ pattern.operationalConfidence = Number(
337
+ (
338
+ (pattern.confidence * decayFactor) /
339
+ (1 + (pattern.rejectionCount ?? 0))
340
+ ).toFixed(6),
341
+ );
342
+ if (
343
+ positive.length === 0 ||
344
+ expired ||
345
+ pattern.operationalConfidence < descriptor.staleConfidenceFloor
346
+ ) {
347
+ pattern.status = WIKI_PATTERN_STATUS.STALE;
348
+ } else if (negative.length > 0 || pattern.contradicts.length > 0)
349
+ pattern.status = WIKI_PATTERN_STATUS.CONTRADICTED;
350
+ else if (hasGrader || domains.length >= descriptor.minCorroboratingSources)
351
+ pattern.status = WIKI_PATTERN_STATUS.CORROBORATED;
352
+ else pattern.status = WIKI_PATTERN_STATUS.HYPOTHESIS;
353
+ pattern.trustDomains = domains;
354
+ pattern.evidenceCounts = {
355
+ positive: positive.length,
356
+ negative: negative.length,
357
+ trustDomains: domains.length,
358
+ };
359
+ pattern.actionable =
360
+ pattern.status === WIKI_PATTERN_STATUS.CORROBORATED &&
361
+ pattern.procedure != null &&
362
+ (pattern.rejectionCount ?? 0) === 0;
363
+ }
364
+ state.evidenceDependents = Object.fromEntries(
365
+ Object.entries(dependents)
366
+ .sort()
367
+ .map(([ref, ids]) => [ref, [...new Set(ids)].sort()]),
368
+ );
369
+ state.index = Object.values(state.patterns)
370
+ .filter(
371
+ (pattern) =>
372
+ ![
373
+ WIKI_PATTERN_STATUS.REVOKED,
374
+ WIKI_PATTERN_STATUS.TOMBSTONED,
375
+ WIKI_PATTERN_STATUS.STALE,
376
+ ].includes(pattern.status),
377
+ )
378
+ .map((pattern) => ({
379
+ patternId: pattern.patternId,
380
+ kind: pattern.kind,
381
+ status: pattern.status,
382
+ summary: pattern.summary,
383
+ confidence: pattern.operationalConfidence,
384
+ actionable: pattern.actionable,
385
+ skillNames: pattern.skillNames,
386
+ }))
387
+ .sort((a, b) => a.patternId.localeCompare(b.patternId));
388
+ }
389
+
390
+ function applyUpsert(state, operation, evidenceByRef, effectiveAt) {
391
+ const proposed = normalizePattern(operation.pattern);
392
+ for (const ref of [
393
+ ...proposed.positiveEvidence,
394
+ ...proposed.negativeEvidence,
395
+ ]) {
396
+ if (!evidenceByRef.has(ref) && !state.evidence[ref])
397
+ throw new Error(`pattern references unresolved evidence: ${ref}`);
398
+ }
399
+ const duplicate = Object.values(state.patterns).find(
400
+ (item) =>
401
+ item.status !== WIKI_PATTERN_STATUS.TOMBSTONED &&
402
+ fingerprint(item) === fingerprint(proposed),
403
+ );
404
+ const existing = duplicate ?? state.patterns[proposed.patternId];
405
+ const patternId = existing?.patternId ?? proposed.patternId;
406
+ state.patterns[patternId] = {
407
+ ...proposed,
408
+ patternId,
409
+ positiveEvidence: [
410
+ ...new Set([
411
+ ...(existing?.positiveEvidence ?? []),
412
+ ...proposed.positiveEvidence,
413
+ ]),
414
+ ].sort(),
415
+ negativeEvidence: [
416
+ ...new Set([
417
+ ...(existing?.negativeEvidence ?? []),
418
+ ...proposed.negativeEvidence,
419
+ ]),
420
+ ].sort(),
421
+ contradicts: [
422
+ ...new Set([...(existing?.contradicts ?? []), ...proposed.contradicts]),
423
+ ].sort(),
424
+ supersedes: [
425
+ ...new Set([...(existing?.supersedes ?? []), ...proposed.supersedes]),
426
+ ].sort(),
427
+ rejectionCount: existing?.rejectionCount ?? 0,
428
+ status: WIKI_PATTERN_STATUS.HYPOTHESIS,
429
+ actionable: false,
430
+ evidenceCounts: { positive: 0, negative: 0, trustDomains: 0 },
431
+ operationalConfidence: proposed.confidence,
432
+ updatedAt: effectiveAt,
433
+ };
434
+ appendLog(
435
+ state,
436
+ duplicate
437
+ ? "pattern-deduplicated"
438
+ : existing
439
+ ? "pattern-updated"
440
+ : "pattern-created",
441
+ patternId,
442
+ effectiveAt,
443
+ duplicate && duplicate.patternId !== proposed.patternId
444
+ ? { discardedPatternId: proposed.patternId }
445
+ : {},
446
+ );
447
+ }
448
+
449
+ function applyOperation(state, operation, evidenceByRef, effectiveAt) {
450
+ if (!OPERATIONS.has(operation?.type))
451
+ throw new TypeError("Wiki maintenance operation is invalid");
452
+ if (operation.type === "upsert")
453
+ return applyUpsert(state, operation, evidenceByRef, effectiveAt);
454
+ if (operation.type === "merge") {
455
+ const target =
456
+ state.patterns[
457
+ requiredString(operation.targetPatternId, "targetPatternId")
458
+ ];
459
+ const sources = stringList(operation.sourcePatternIds, "sourcePatternIds", {
460
+ allowEmpty: false,
461
+ });
462
+ if (
463
+ !target ||
464
+ sources.some(
465
+ (id) =>
466
+ !state.patterns[id] ||
467
+ id === target.patternId ||
468
+ state.patterns[id].kind !== target.kind,
469
+ )
470
+ ) {
471
+ throw new Error(
472
+ "merge patterns are missing, cyclic, or belong to different kinds",
473
+ );
474
+ }
475
+ for (const id of sources) {
476
+ const source = state.patterns[id];
477
+ target.positiveEvidence = [
478
+ ...new Set([...target.positiveEvidence, ...source.positiveEvidence]),
479
+ ].sort();
480
+ target.negativeEvidence = [
481
+ ...new Set([...target.negativeEvidence, ...source.negativeEvidence]),
482
+ ].sort();
483
+ source.status = WIKI_PATTERN_STATUS.REVOKED;
484
+ source.revocationReason = `merged-into:${target.patternId}`;
485
+ target.supersedes = [...new Set([...target.supersedes, id])].sort();
486
+ }
487
+ appendLog(state, "patterns-merged", target.patternId, effectiveAt, {
488
+ sourcePatternIds: sources,
489
+ });
490
+ return;
491
+ }
492
+ if (operation.type === "revoke" || operation.type === "tombstone") {
493
+ const pattern =
494
+ state.patterns[requiredString(operation.patternId, "patternId")];
495
+ if (!pattern) throw new Error("cannot revoke an unknown pattern");
496
+ pattern.status =
497
+ operation.type === "revoke"
498
+ ? WIKI_PATTERN_STATUS.REVOKED
499
+ : WIKI_PATTERN_STATUS.TOMBSTONED;
500
+ pattern.revocationReason = requiredString(operation.reason, "reason");
501
+ pattern.actionable = false;
502
+ appendLog(
503
+ state,
504
+ `pattern-${operation.type}d`,
505
+ pattern.patternId,
506
+ effectiveAt,
507
+ { reason: pattern.revocationReason },
508
+ );
509
+ return;
510
+ }
511
+ const decision = operation.decision;
512
+ if (!decision || !["accepted", "rejected"].includes(decision.outcome))
513
+ throw new TypeError("proposal impact decision is invalid");
514
+ const receiptRef = requiredString(decision.receiptRef, "decision.receiptRef");
515
+ const evidence = evidenceByRef.get(receiptRef) ?? state.evidence[receiptRef];
516
+ if (
517
+ evidence?.kind !== "proposal-decision" ||
518
+ !evidenceUsable(evidence, effectiveAt)
519
+ )
520
+ throw new Error("proposal impact requires an active decision receipt");
521
+ const skillName = requiredString(decision.skillName, "decision.skillName");
522
+ const patternRefs = stringList(decision.patternRefs, "decision.patternRefs");
523
+ const decisionCore = {
524
+ candidateId: requiredString(decision.candidateId, "decision.candidateId"),
525
+ skillName,
526
+ outcome: decision.outcome,
527
+ patternRefs,
528
+ reason: requiredString(decision.reason, "decision.reason"),
529
+ };
530
+ if (evidence.data?.decisionDigest !== hash(decisionCore)) {
531
+ throw new Error(
532
+ "proposal impact fields are not bound to the decision receipt",
533
+ );
534
+ }
535
+ const record = (state.skillImpact[skillName] ??= {
536
+ accepted: 0,
537
+ rejected: 0,
538
+ decisions: [],
539
+ });
540
+ record[decision.outcome] += 1;
541
+ record.decisions.push({
542
+ ...decisionCore,
543
+ receiptRef,
544
+ decidedAt: effectiveAt,
545
+ });
546
+ for (const patternId of patternRefs) {
547
+ const pattern = state.patterns[patternId];
548
+ if (!pattern)
549
+ throw new Error(
550
+ `proposal decision references unknown pattern: ${patternId}`,
551
+ );
552
+ if (decision.outcome === "rejected")
553
+ pattern.rejectionCount = (pattern.rejectionCount ?? 0) + 1;
554
+ }
555
+ appendLog(
556
+ state,
557
+ "proposal-impact-recorded",
558
+ decisionCore.candidateId,
559
+ effectiveAt,
560
+ { skillName, outcome: decision.outcome, patternRefs },
561
+ );
562
+ }
563
+
564
+ function normalizeDescriptor(input) {
565
+ const decayHalfLifeDays = Number(input?.decayHalfLifeDays ?? 30);
566
+ const staleConfidenceFloor = Number(input?.staleConfidenceFloor ?? 0.2);
567
+ if (
568
+ !Number.isFinite(decayHalfLifeDays) ||
569
+ decayHalfLifeDays <= 0 ||
570
+ !Number.isFinite(staleConfidenceFloor) ||
571
+ staleConfidenceFloor < 0 ||
572
+ staleConfidenceFloor > 1
573
+ ) {
574
+ throw new TypeError("Wiki decay policy is invalid");
575
+ }
576
+ return freeze({
577
+ tenantId: requiredString(input?.tenantId, "tenantId"),
578
+ evolutionRunId: requiredString(input?.evolutionRunId, "evolutionRunId"),
579
+ maintainerModel: requiredString(input?.maintainerModel, "maintainerModel"),
580
+ rulesDigest: DIGEST.test(input?.rulesDigest ?? "")
581
+ ? input.rulesDigest
582
+ : (() => {
583
+ throw new TypeError("rulesDigest must be sha256-bound");
584
+ })(),
585
+ minCorroboratingSources: Math.max(
586
+ 2,
587
+ Number(input?.minCorroboratingSources) || 2,
588
+ ),
589
+ decayHalfLifeDays,
590
+ staleConfidenceFloor,
591
+ });
592
+ }
593
+
594
+ export class EvidenceBackedWikiMaintainer {
595
+ constructor({ descriptor, policy, ports } = {}) {
596
+ this.descriptor = normalizeDescriptor(descriptor);
597
+ if (
598
+ policy?.trustedProjectionRead !== true ||
599
+ policy?.rawEvidenceRead === true ||
600
+ policy?.activeSkillWrite === true ||
601
+ policy?.shell === true ||
602
+ policy?.network === true ||
603
+ policy?.secretRead === true
604
+ ) {
605
+ throw new Error(
606
+ "Maintainer policy must allow only trusted projection reads and Wiki revision writes",
607
+ );
608
+ }
609
+ for (const name of [
610
+ "loadWiki",
611
+ "resolveEvidence",
612
+ "derive",
613
+ "commitRevision",
614
+ ]) {
615
+ if (typeof ports?.[name] !== "function")
616
+ throw new TypeError(`${name} port is required`);
617
+ }
618
+ this._ports = ports;
619
+ Object.freeze(this);
620
+ }
621
+
622
+ async maintain({ evidenceRefs, effectiveAt, maintenanceRequest } = {}) {
623
+ const timestamp = validTime(effectiveAt, "effectiveAt");
624
+ const refs = stringList(evidenceRefs, "evidenceRefs", {
625
+ allowEmpty: false,
626
+ max: 256,
627
+ });
628
+ const request = normalizeMaintenanceRequest(
629
+ maintenanceRequest,
630
+ this.descriptor.tenantId,
631
+ );
632
+ const stateEnvelope = await this._ports.loadWiki({
633
+ tenantId: this.descriptor.tenantId,
634
+ });
635
+ const state = verifyStateEnvelope(stateEnvelope, this.descriptor.tenantId);
636
+ const priorStateDigest = stateEnvelope.stateDigest;
637
+ state.maintenanceRequests ??= {};
638
+ if (request) {
639
+ const existing = state.maintenanceRequests[request.requestId];
640
+ if (existing) {
641
+ if (
642
+ existing.requestDigest !== request.requestDigest ||
643
+ canonical(existing.evidenceRefs) !== canonical(refs) ||
644
+ existing.effectiveAt !== timestamp ||
645
+ !Number.isSafeInteger(existing.revision) ||
646
+ !REVISION_ID.test(existing.revisionId ?? "")
647
+ ) {
648
+ const error = new Error(
649
+ "maintenance request id is already bound to different work",
650
+ );
651
+ error.code = "WIKI_MAINTENANCE_REQUEST_CONFLICT";
652
+ throw error;
653
+ }
654
+ return freeze({
655
+ revisionId: existing.revisionId,
656
+ revision: existing.revision,
657
+ currentRevision: state.revision,
658
+ stateDigest: priorStateDigest,
659
+ state: clone(state),
660
+ maintenanceRequestId: request.requestId,
661
+ recovered: true,
662
+ });
663
+ }
664
+ }
665
+ const evidenceByRef = new Map();
666
+ for (const ref of refs) {
667
+ const evidence = verifyEvidence(
668
+ await this._ports.resolveEvidence(ref),
669
+ this.descriptor.tenantId,
670
+ ref,
671
+ );
672
+ evidenceByRef.set(ref, evidence);
673
+ state.evidence[ref] = Object.fromEntries(
674
+ Object.entries(evidence).filter(([key]) => key !== "envelopeDigest"),
675
+ );
676
+ }
677
+ const derived = await this._ports.derive(
678
+ freeze({
679
+ descriptor: this.descriptor,
680
+ state: clone(state),
681
+ evidence: [...evidenceByRef.values()],
682
+ effectiveAt: timestamp,
683
+ }),
684
+ );
685
+ if (!Array.isArray(derived?.operations) || derived.operations.length > 128)
686
+ throw new TypeError("derive must return a bounded operations list");
687
+ for (const operation of derived.operations)
688
+ applyOperation(state, clone(operation), evidenceByRef, timestamp);
689
+ rebuildDerivedState(state, timestamp, this.descriptor);
690
+ state.revision += 1;
691
+ const revisionPayload = {
692
+ schema: WIKI_REVISION_SCHEMA,
693
+ tenantId: this.descriptor.tenantId,
694
+ evolutionRunId: this.descriptor.evolutionRunId,
695
+ revision: state.revision,
696
+ priorStateDigest,
697
+ rulesDigest: this.descriptor.rulesDigest,
698
+ maintainerModel: this.descriptor.maintainerModel,
699
+ effectiveAt: timestamp,
700
+ evidenceRefs: refs,
701
+ operationDigest: hash(derived.operations),
702
+ maintenanceRequestId: request?.requestId ?? null,
703
+ maintenanceRequestDigest: request?.requestDigest ?? null,
704
+ };
705
+ state.revisionId = `wiki:${hash(revisionPayload).slice(7)}`;
706
+ if (request) {
707
+ state.maintenanceRequests[request.requestId] = {
708
+ requestDigest: request.requestDigest,
709
+ evidenceRefs: refs,
710
+ effectiveAt: timestamp,
711
+ operationDigest: revisionPayload.operationDigest,
712
+ revision: state.revision,
713
+ revisionId: state.revisionId,
714
+ };
715
+ }
716
+ const stateDigest = hash(state);
717
+ const revision = freeze({
718
+ ...revisionPayload,
719
+ revisionId: state.revisionId,
720
+ stateDigest,
721
+ state: freeze(clone(state)),
722
+ });
723
+ const committed = await this._ports.commitRevision(
724
+ freeze({ expectedStateDigest: priorStateDigest, revision }),
725
+ );
726
+ if (
727
+ committed?.committed !== true ||
728
+ committed?.revisionId !== revision.revisionId ||
729
+ committed?.stateDigest !== stateDigest ||
730
+ committed?.evolutionRunId !== this.descriptor.evolutionRunId
731
+ ) {
732
+ const error = new Error(
733
+ "Wiki persistence did not confirm the exact revision and EvolutionRun binding",
734
+ );
735
+ error.code = "WIKI_MAINTAINER_COMMIT_UNCONFIRMED";
736
+ throw error;
737
+ }
738
+ return freeze({
739
+ revisionId: revision.revisionId,
740
+ revision: state.revision,
741
+ currentRevision: state.revision,
742
+ stateDigest,
743
+ state: clone(state),
744
+ maintenanceRequestId: request?.requestId ?? null,
745
+ recovered: false,
746
+ });
747
+ }
748
+ }
749
+
750
+ export function createEvidenceBackedWikiMaintainer(options) {
751
+ return new EvidenceBackedWikiMaintainer(options);
752
+ }