chainlesschain 0.166.9 → 0.166.13

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 (232) hide show
  1. package/README.md +33 -0
  2. package/bin/chainlesschain.js +5 -0
  3. package/package.json +5 -2
  4. package/src/assets/web-panel/assets/{AIOps-DJvpifGx.js → AIOps-BN25cNOD.js} +1 -1
  5. package/src/assets/web-panel/assets/{ActionButton-D2UhuF_A.js → ActionButton-Da9IP-m5.js} +1 -1
  6. package/src/assets/web-panel/assets/{Analytics-C9ho9obu.js → Analytics-D7hkEvQm.js} +3 -3
  7. package/src/assets/web-panel/assets/{AppLayout-BvRCpFK3.js → AppLayout-CCV4Xnbc.js} +5 -5
  8. package/src/assets/web-panel/assets/{Artifacts-jxrySvpk.js → Artifacts-CVSnVJeA.js} +1 -1
  9. package/src/assets/web-panel/assets/{Audit-ovEw0ZYl.js → Audit-CuYdw5Ag.js} +1 -1
  10. package/src/assets/web-panel/assets/{BackgroundAgents-D7H5z6zS.js → BackgroundAgents-DZCnK-Tf.js} +1 -1
  11. package/src/assets/web-panel/assets/{Backup-BuXob8za.js → Backup-cV9FdpTy.js} +1 -1
  12. package/src/assets/web-panel/assets/{BaseInput-CtnszuQ2.js → BaseInput-BRnv6YSp.js} +1 -1
  13. package/src/assets/web-panel/assets/{Chat-B_mYE8fJ.js → Chat-Bw5w87P4.js} +6 -6
  14. package/src/assets/web-panel/assets/ChatBubbleRenderer-C0GjtUAw.js +1 -0
  15. package/src/assets/web-panel/assets/{Checkbox-ierdtcJD.js → Checkbox-DaoTqcdt.js} +1 -1
  16. package/src/assets/web-panel/assets/{Codegen-3VKPJD_B.js → Codegen-B4dYcEIi.js} +1 -1
  17. package/src/assets/web-panel/assets/{Col-1EdMyf5P.js → Col-mAhuOeuD.js} +1 -1
  18. package/src/assets/web-panel/assets/{Community-D6H7UA8K.js → Community-BK6KEdBk.js} +1 -1
  19. package/src/assets/web-panel/assets/{Compact-BBAZvYIm.js → Compact-CCglmNu5.js} +1 -1
  20. package/src/assets/web-panel/assets/{Compliance-CgsTrAoj.js → Compliance-Cm8S4of7.js} +1 -1
  21. package/src/assets/web-panel/assets/{Cowork-tnZgLoTE.js → Cowork-Dhs1QLMe.js} +3 -3
  22. package/src/assets/web-panel/assets/{Cron-CPpZw2Nh.js → Cron-CLlhmXWk.js} +2 -2
  23. package/src/assets/web-panel/assets/{Crosschain-BymBcp5z.js → Crosschain-BnHVbaNs.js} +1 -1
  24. package/src/assets/web-panel/assets/{DID-DKPxS_K1.js → DID-UzQP9-0O.js} +2 -2
  25. package/src/assets/web-panel/assets/{Dashboard-DvJhEmH9.js → Dashboard-CBdlPClz.js} +2 -2
  26. package/src/assets/web-panel/assets/{Dropdown-b8iQyQIZ.js → Dropdown-axIRPdq1.js} +1 -1
  27. package/src/assets/web-panel/assets/{EmailListRenderer--0-A-zKb.js → EmailListRenderer-DUalW5rx.js} +1 -1
  28. package/src/assets/web-panel/assets/{FamilyGuardDashboard-C12iDN06.js → FamilyGuardDashboard-Bl4W9luY.js} +1 -1
  29. package/src/assets/web-panel/assets/{Federation-BRhaE2Z8.js → Federation-C1yP-uEu.js} +1 -1
  30. package/src/assets/web-panel/assets/{FormItemContext-C4dnq7yf.js → FormItemContext-Xh6Ig5q8.js} +1 -1
  31. package/src/assets/web-panel/assets/{GenericCardRenderer-CGAdbR5n.js → GenericCardRenderer-C2cz3bt2.js} +1 -1
  32. package/src/assets/web-panel/assets/{Git-4eA2fYzL.js → Git-C_z63ixg.js} +2 -2
  33. package/src/assets/web-panel/assets/{Governance-BJuY9Y-9.js → Governance-BYqB33_3.js} +1 -1
  34. package/src/assets/web-panel/assets/{Inference-DZ5Dy4up.js → Inference-CEULuArm.js} +1 -1
  35. package/src/assets/web-panel/assets/{KnowledgeGraph-BMTjRs4A.js → KnowledgeGraph-fgMsu-0H.js} +1 -1
  36. package/src/assets/web-panel/assets/{Logs-BKEeQEfJ.js → Logs-DO9Hdcec.js} +2 -2
  37. package/src/assets/web-panel/assets/MarkdownRenderer-BkrK04xW.js +1 -0
  38. package/src/assets/web-panel/assets/{Marketplace-Dhho4fEg.js → Marketplace-B8zyLiFe.js} +1 -1
  39. package/src/assets/web-panel/assets/{McpTools-BW6xxSlU.js → McpTools-BLAN2mWW.js} +3 -3
  40. package/src/assets/web-panel/assets/{Memory-mPqlpSkq.js → Memory-CrQcFM7u.js} +2 -2
  41. package/src/assets/web-panel/assets/{MobileBridge-Cb9N_DQI.js → MobileBridge-DVnyiiU-.js} +1 -1
  42. package/src/assets/web-panel/assets/{MobileProjects-BNs8tah_.js → MobileProjects-qjCME5OD.js} +1 -1
  43. package/src/assets/web-panel/assets/{Mtc-CaxBt8tn.js → Mtc-4JE3C3It.js} +2 -2
  44. package/src/assets/web-panel/assets/{MtcAudit-DRPQbp3t.js → MtcAudit-CjBuak9I.js} +5 -5
  45. package/src/assets/web-panel/assets/{Multisig-CRm-d8bK.js → Multisig-BtPcYQcC.js} +3 -3
  46. package/src/assets/web-panel/assets/{NLProgramming-BCCk3LPs.js → NLProgramming-Cx653_Ba.js} +1 -1
  47. package/src/assets/web-panel/assets/{Notes-Cv8SN8gN.js → Notes-CkuD3YzS.js} +4 -4
  48. package/src/assets/web-panel/assets/{NotificationSettings-CggIvTh5.js → NotificationSettings-BgNgU7HH.js} +1 -1
  49. package/src/assets/web-panel/assets/{OrderTableRenderer-BW3_bV12.js → OrderTableRenderer-BTC8W0a-.js} +1 -1
  50. package/src/assets/web-panel/assets/{Organization-BuRE-VCO.js → Organization-Dmd4Hjxz.js} +4 -4
  51. package/src/assets/web-panel/assets/{Overflow-DBrm1gju.js → Overflow-C_5DA-G0.js} +1 -1
  52. package/src/assets/web-panel/assets/{P2P-CWVnDYgS.js → P2P-uwslV4XC.js} +2 -2
  53. package/src/assets/web-panel/assets/{PdhVaultBrowser-8RKUIpte.js → PdhVaultBrowser-CuNgwVnX.js} +5 -5
  54. package/src/assets/web-panel/assets/{Permissions-tDUAxogW.js → Permissions-CJayVX9H.js} +4 -4
  55. package/src/assets/web-panel/assets/{PersonalDataHub-Dv2-CFis.js → PersonalDataHub-Cb05H5go.js} +4 -4
  56. package/src/assets/web-panel/assets/{Pipeline-DM6kvFni.js → Pipeline-BZlGKapZ.js} +1 -1
  57. package/src/assets/web-panel/assets/{Privacy-DzK-yL0d.js → Privacy-BIzZWYzd.js} +1 -1
  58. package/src/assets/web-panel/assets/{ProjectInit-B5UrNpkj.js → ProjectInit-vEYYyQXQ.js} +2 -2
  59. package/src/assets/web-panel/assets/{ProjectSettings-CxJD43oW.js → ProjectSettings-MZqTy5CC.js} +2 -2
  60. package/src/assets/web-panel/assets/Projects-CRARUOl8.js +1 -0
  61. package/src/assets/web-panel/assets/{Providers-CYl4jRYK.js → Providers-80VAK8PX.js} +1 -1
  62. package/src/assets/web-panel/assets/{QrScannerModal-DeH2T0fH.js → QrScannerModal-CzRSoIwK.js} +1 -1
  63. package/src/assets/web-panel/assets/{QuickAsk-Bw9k_kTk.js → QuickAsk-BpwQ2n4C.js} +1 -1
  64. package/src/assets/web-panel/assets/{Recommend-Bl_jGwoi.js → Recommend-BC03TZ79.js} +1 -1
  65. package/src/assets/web-panel/assets/{RemoteSession-d6u01Kq0.js → RemoteSession-DE59-jaa.js} +2 -2
  66. package/src/assets/web-panel/assets/{Reputation-Ds_Dfr-k.js → Reputation-DOHmeWyA.js} +1 -1
  67. package/src/assets/web-panel/assets/{Row-BpQUQTNj.js → Row-hc5RZl1D.js} +1 -1
  68. package/src/assets/web-panel/assets/{RssFeed-BatpZBGt.js → RssFeed-Cv5uMP7E.js} +3 -3
  69. package/src/assets/web-panel/assets/{Search-D7hOObzi.js → Search-qohu7Ug0.js} +1 -1
  70. package/src/assets/web-panel/assets/{Security-dXQ4FKNt.js → Security-drCPFWV5.js} +4 -4
  71. package/src/assets/web-panel/assets/{Services-D8wInGPa.js → Services-BAEgUeRy.js} +2 -2
  72. package/src/assets/web-panel/assets/{Skeleton-DXQCoYpM.js → Skeleton-CkJkKBlo.js} +1 -1
  73. package/src/assets/web-panel/assets/{Skills-D3oLRIVn.js → Skills-bMuKEpez.js} +1 -1
  74. package/src/assets/web-panel/assets/{Sla-BmP4-LnJ.js → Sla-DxWQVHRC.js} +1 -1
  75. package/src/assets/web-panel/assets/{SpeechSettings-DLj-xzro.js → SpeechSettings-BpS8idmV.js} +1 -1
  76. package/src/assets/web-panel/assets/{SyncSettings-8KKaaRMr.js → SyncSettings-BPBLlW5v.js} +2 -2
  77. package/src/assets/web-panel/assets/{Tasks-GyK42I7C.js → Tasks-C4SLEeUh.js} +1 -1
  78. package/src/assets/web-panel/assets/{Templates-n1y_8UBG.js → Templates-r-wfU39U.js} +1 -1
  79. package/src/assets/web-panel/assets/{Tenant-B39nBph8.js → Tenant-C7vjRmLN.js} +1 -1
  80. package/src/assets/web-panel/assets/{Terminal-DCQoOKXe.js → Terminal-DdzeFCV2.js} +2 -2
  81. package/src/assets/web-panel/assets/{TimelineRenderer-BkhbDfXk.js → TimelineRenderer-CYyWN2KD.js} +1 -1
  82. package/src/assets/web-panel/assets/{Tokens-BeHIIU8D.js → Tokens-D8A9osUg.js} +1 -1
  83. package/src/assets/web-panel/assets/{Trigger-BHvWXVOb.js → Trigger-DBvrNlyS.js} +1 -1
  84. package/src/assets/web-panel/assets/{Trust-y2WaOSto.js → Trust-DBVAGc7o.js} +1 -1
  85. package/src/assets/web-panel/assets/{UkeySign-DBFeJBzt.js → UkeySign-C1vQUCRJ.js} +1 -1
  86. package/src/assets/web-panel/assets/{VideoEditing-BEOWitC-.js → VideoEditing-BKs4dah4.js} +1 -1
  87. package/src/assets/web-panel/assets/{Wallet-BITedTe6.js → Wallet-ByIuj3lD.js} +4 -4
  88. package/src/assets/web-panel/assets/{WebAuthn-DMwlJVin.js → WebAuthn-DYlAFCm9.js} +3 -3
  89. package/src/assets/web-panel/assets/{WorkflowEditor-CKfGFEu1.js → WorkflowEditor-Dwyadd0c.js} +1 -1
  90. package/src/assets/web-panel/assets/{chat-DR6gnBGF.js → chat-Bm2f3s08.js} +1 -1
  91. package/src/assets/web-panel/assets/{colors-C_Hw5NCn.js → colors-C8ORSh7V.js} +1 -1
  92. package/src/assets/web-panel/assets/{compact-item-B8IPwsEu.js → compact-item-1iVlnsM-.js} +1 -1
  93. package/src/assets/web-panel/assets/{createContext-D-u4JVnJ.js → createContext-CmqNyUSL.js} +1 -1
  94. package/src/assets/web-panel/assets/devWarning-B69RCDDH.js +1 -0
  95. package/src/assets/web-panel/assets/{hasIn-CZubdS4D.js → hasIn-rUMaX3Ul.js} +1 -1
  96. package/src/assets/web-panel/assets/index-9biMQ-tw.js +1 -0
  97. package/src/assets/web-panel/assets/{index-C25rOz7O.js → index-B53c3diB.js} +1 -1
  98. package/src/assets/web-panel/assets/{index-CfniPP5p.js → index-B59OWAs7.js} +1 -1
  99. package/src/assets/web-panel/assets/{index-11ASqx-A.js → index-B8BrB0Ls.js} +1 -1
  100. package/src/assets/web-panel/assets/{index-auyNYjLo.js → index-BC32_0xT.js} +1 -1
  101. package/src/assets/web-panel/assets/{index-Ctje8Vl3.js → index-BC8Wb6cy.js} +1 -1
  102. package/src/assets/web-panel/assets/{index-Cg2bCeol.js → index-BCOhTILd.js} +1 -1
  103. package/src/assets/web-panel/assets/{index-D1w96gg9.js → index-BFniLMn9.js} +1 -1
  104. package/src/assets/web-panel/assets/{index-k1Xa5iw1.js → index-BIvM-uAX.js} +1 -1
  105. package/src/assets/web-panel/assets/{index-DuOU5apg.js → index-BK-RY0-7.js} +1 -1
  106. package/src/assets/web-panel/assets/{index-D47jt8ET.js → index-BQL9r73Y.js} +1 -1
  107. package/src/assets/web-panel/assets/{index-C2VmtNaQ.js → index-BVtnizyC.js} +1 -1
  108. package/src/assets/web-panel/assets/{index-Bizm87cX.js → index-Bf1PlIHp.js} +1 -1
  109. package/src/assets/web-panel/assets/{index-Dt-w3xQ7.js → index-Bgw4Y2rU.js} +1 -1
  110. package/src/assets/web-panel/assets/{index-ffe7bvqP.js → index-BgwLNxmr.js} +1 -1
  111. package/src/assets/web-panel/assets/{index-Cl5RpreD.js → index-C-J5-pGH.js} +1 -1
  112. package/src/assets/web-panel/assets/{index-idZKyADQ.js → index-C-ZIHi42.js} +1 -1
  113. package/src/assets/web-panel/assets/{index-LvjAKDSl.js → index-C3B1S-mW.js} +1 -1
  114. package/src/assets/web-panel/assets/{index-DmRcgiYT.js → index-C5RfRciU.js} +1 -1
  115. package/src/assets/web-panel/assets/{index-rYBHSDcJ.js → index-CCEyz58d.js} +1 -1
  116. package/src/assets/web-panel/assets/index-COie87kl.js +1 -0
  117. package/src/assets/web-panel/assets/{index-DcFjRT-K.js → index-CTfEmKK0.js} +1 -1
  118. package/src/assets/web-panel/assets/{index-DjRrqooR.js → index-CfiHyVyl.js} +1 -1
  119. package/src/assets/web-panel/assets/{index-DpjHpRoq.js → index-CkFvuAex.js} +3 -3
  120. package/src/assets/web-panel/assets/{index-BzoFanEQ.js → index-D98FXb0w.js} +1 -1
  121. package/src/assets/web-panel/assets/{index-Cbm8F0M2.js → index-DDHk911Q.js} +1 -1
  122. package/src/assets/web-panel/assets/{index-BXS6wYJZ.js → index-DJxtPFOh.js} +1 -1
  123. package/src/assets/web-panel/assets/{index-DY6MHo9D.js → index-DVJ1uKf4.js} +1 -1
  124. package/src/assets/web-panel/assets/{index-CiXkhOkY.js → index-Dcck8QfW.js} +1 -1
  125. package/src/assets/web-panel/assets/{index-mYZJxGH6.js → index-DgUE51Pz.js} +1 -1
  126. package/src/assets/web-panel/assets/{index-CqmLw8cs.js → index-Dsve5Gkv.js} +1 -1
  127. package/src/assets/web-panel/assets/{index-xoNssgz_.js → index-DvedhJeM.js} +1 -1
  128. package/src/assets/web-panel/assets/{index-DfnzrUxU.js → index-DwMdWKiF.js} +1 -1
  129. package/src/assets/web-panel/assets/{index-Bi21Etg4.js → index-OcbzdRpT.js} +1 -1
  130. package/src/assets/web-panel/assets/{index-DJ6pG79m.js → index-fPK6GFEg.js} +1 -1
  131. package/src/assets/web-panel/assets/{index-_nL79NT8.js → index-geQinnns.js} +1 -1
  132. package/src/assets/web-panel/assets/{index-DTosx2Wu.js → index-pHWA7bmM.js} +1 -1
  133. package/src/assets/web-panel/assets/{index-cVqbas5C.js → index-reUHhuz8.js} +1 -1
  134. package/src/assets/web-panel/assets/{index-Bv2132PO.js → index-tqTx5wXo.js} +1 -1
  135. package/src/assets/web-panel/assets/{initDefaultProps-lcU_dT2r.js → initDefaultProps-CuA-cpkL.js} +1 -1
  136. package/src/assets/web-panel/assets/{motion-Dc7UNXZ1.js → motion-CimmE_9b.js} +1 -1
  137. package/src/assets/web-panel/assets/{move-Cll8ZXaL.js → move-DlGlrltz.js} +1 -1
  138. package/src/assets/web-panel/assets/{mtc-parser-CAb_yLIM.js → mtc-parser-CK6BJQ07.js} +1 -1
  139. package/src/assets/web-panel/assets/{omit-DurYMyTe.js → omit-BKjBfZfB.js} +1 -1
  140. package/src/assets/web-panel/assets/{pickAttrs-BITQyOlw.js → pickAttrs-B2chVYw9.js} +1 -1
  141. package/src/assets/web-panel/assets/{placementArrow-Dg1rH91i.js → placementArrow-BW8aLJJ3.js} +1 -1
  142. package/src/assets/web-panel/assets/{responsiveObserve-D_EWIeac.js → responsiveObserve-dXlPl66S.js} +1 -1
  143. package/src/assets/web-panel/assets/{slide-lK4d3zsh.js → slide-hSa89S4G.js} +1 -1
  144. package/src/assets/web-panel/assets/{statusUtils-Bflq-Git.js → statusUtils-DM6KLt-s.js} +1 -1
  145. package/src/assets/web-panel/assets/{styleChecker-Ccp0I-wt.js → styleChecker-C9MQAK15.js} +1 -1
  146. package/src/assets/web-panel/assets/{useFlexGapSupport-CYZbByYA.js → useFlexGapSupport-BpQDpP0z.js} +1 -1
  147. package/src/assets/web-panel/assets/{useFs-B_x0nxFD.js → useFs-C7rtRViA.js} +1 -1
  148. package/src/assets/web-panel/assets/{usePersonalDataHub-DW55rL85.js → usePersonalDataHub-Dm15xafq.js} +1 -1
  149. package/src/assets/web-panel/assets/{vnode-Ca6MH3W8.js → vnode-mEiEytw4.js} +1 -1
  150. package/src/assets/web-panel/assets/{zoom-NdHelOog.js → zoom-DzPxflhY.js} +1 -1
  151. package/src/assets/web-panel/index.html +1 -1
  152. package/src/command-help-index.json +5 -5
  153. package/src/commands/agent.js +51 -20
  154. package/src/commands/compact.js +349 -3
  155. package/src/commands/context.js +72 -1
  156. package/src/commands/graph.js +26 -15
  157. package/src/commands/hmemory.js +21 -0
  158. package/src/commands/hook.js +256 -28
  159. package/src/commands/memory.js +209 -42
  160. package/src/commands/promcomp.js +17 -0
  161. package/src/commands/serve.js +94 -10
  162. package/src/commands/session.js +2 -3
  163. package/src/commands/team.js +7 -1
  164. package/src/data/changelog.json +41 -2
  165. package/src/gateways/ws/session-core-protocol.js +62 -3
  166. package/src/gateways/ws/session-protocol.js +123 -15
  167. package/src/gateways/ws/ws-agent-handler.js +157 -9
  168. package/src/gateways/ws/ws-session-gateway.js +204 -30
  169. package/src/generated/cc-agent-protocol.schema.json +2094 -4
  170. package/src/harness/prompt-compressor.js +5 -55
  171. package/src/lib/agent-sandbox.js +14 -4
  172. package/src/lib/agent-team/team-distributed-graph-bridge.js +3 -3
  173. package/src/lib/app-server/context-memory-notifications.js +54 -0
  174. package/src/lib/app-server/rollout-store-factory.js +115 -0
  175. package/src/lib/app-server/rollout-store.js +310 -23
  176. package/src/lib/app-server/server.js +184 -7
  177. package/src/lib/app-server/sqlite-rollout-store.js +59 -16
  178. package/src/lib/auto-mode-config.js +26 -0
  179. package/src/lib/causal-observability.js +13 -1
  180. package/src/lib/context-memory-kernel/authority.js +95 -0
  181. package/src/lib/context-memory-kernel/durable-memory-port.js +296 -0
  182. package/src/lib/context-memory-kernel/index.js +8 -0
  183. package/src/lib/context-memory-kernel/jsonl-session-context-port.js +232 -0
  184. package/src/lib/context-memory-kernel/live-compaction.js +375 -0
  185. package/src/lib/context-memory-kernel/memory-service.js +667 -0
  186. package/src/lib/context-memory-kernel/message-adapter.js +451 -0
  187. package/src/lib/context-memory-kernel/privacy-purge-port.js +244 -0
  188. package/src/lib/context-memory-kernel/provider-context.js +192 -0
  189. package/src/lib/context-memory-kernel/runtime.js +61 -0
  190. package/src/lib/direct-model-usage.js +22 -18
  191. package/src/lib/graph-kernel/cutover-authority-resolver.js +4 -5
  192. package/src/lib/graph-kernel/cutover-ledger.js +2 -2
  193. package/src/lib/graph-kernel/event-store.js +3 -5
  194. package/src/lib/graph-kernel/index.js +2 -0
  195. package/src/lib/graph-kernel/migration-saga.js +2 -2
  196. package/src/lib/graph-kernel/production-cutover-evidence.js +976 -0
  197. package/src/lib/graph-kernel/retirement-evidence.js +12 -4
  198. package/src/lib/hierarchical-memory.js +22 -0
  199. package/src/lib/hook-audit-store.js +170 -0
  200. package/src/lib/hook-event-bus.cjs +0 -0
  201. package/src/lib/hook-manager.js +25 -83
  202. package/src/lib/hook-runtime-adapters.js +233 -0
  203. package/src/lib/hook-runtime-contract.cjs +291 -0
  204. package/src/lib/hook-runtime-contract.js +21 -0
  205. package/src/lib/hook-trust.js +210 -0
  206. package/src/lib/hooks-v2-producers.js +52 -6
  207. package/src/lib/hooks-v2-runtime.js +443 -178
  208. package/src/lib/llm-pricing.js +10 -0
  209. package/src/lib/memory-manager.js +29 -4
  210. package/src/lib/model-context-window.js +52 -0
  211. package/src/lib/permanent-memory.js +17 -0
  212. package/src/lib/plan-mode.js +8 -4
  213. package/src/lib/process-execution-broker/index.js +208 -16
  214. package/src/lib/runtime-usage-ledger.js +20 -0
  215. package/src/lib/session-consolidator.js +59 -0
  216. package/src/lib/session-core-singletons.js +17 -4
  217. package/src/lib/session-hooks.js +29 -2
  218. package/src/lib/session-transcript-migration.js +188 -0
  219. package/src/lib/settings-hook-events.cjs +37 -401
  220. package/src/lib/settings-hook-events.js +210 -8
  221. package/src/lib/settings-hooks.cjs +42 -83
  222. package/src/lib/ws-session-manager.js +4 -1
  223. package/src/repl/agent-repl.js +186 -53
  224. package/src/runtime/agent-core.js +376 -350
  225. package/src/runtime/headless-runner.js +40 -33
  226. package/src/runtime/headless-stream.js +131 -30
  227. package/src/assets/web-panel/assets/ChatBubbleRenderer-DaiA6CsX.js +0 -1
  228. package/src/assets/web-panel/assets/MarkdownRenderer-B05DgASC.js +0 -1
  229. package/src/assets/web-panel/assets/Projects-Z2Orm8Ma.js +0 -1
  230. package/src/assets/web-panel/assets/devWarning-Cq6lSd_q.js +0 -1
  231. package/src/assets/web-panel/assets/index-BQ3213fz.js +0 -1
  232. package/src/assets/web-panel/assets/index-vJagfET2.js +0 -1
@@ -0,0 +1,976 @@
1
+ import { GRAPH_CUTOVER_REQUIRED_PLATFORMS } from "./cutover-ledger.js";
2
+ import { graphDigest } from "./compiler.js";
3
+ import {
4
+ graphRuntimeEntryManifestDigest,
5
+ loadGraphRuntimeSurfaceManifest,
6
+ } from "./runtime-surface-manifest.js";
7
+ import {
8
+ normalizeGraphLegacyWriterObservation,
9
+ normalizeGraphRetirementContract,
10
+ normalizeGraphRetirementEvidence,
11
+ } from "./retirement-evidence.js";
12
+
13
+ export const GRAPH_PRODUCTION_CUTOVER_EVIDENCE_SCHEMA =
14
+ "chainlesschain.graph-production-cutover-evidence/v1";
15
+ export const GRAPH_ENTRY_WRITER_OBSERVATION_SCHEMA =
16
+ "chainlesschain.graph-entry-writer-observation/v1";
17
+ export const GRAPH_PRODUCTION_CUTOVER_RECEIPT_SCHEMA =
18
+ "chainlesschain.graph-production-cutover-receipt/v1";
19
+
20
+ export const GRAPH_REQUIRED_CUTOVER_STAGES = Object.freeze([
21
+ "shadow",
22
+ "internal_canary",
23
+ "opt_in_canary",
24
+ "canonical_default",
25
+ "legacy_read_only",
26
+ ]);
27
+ export const GRAPH_REQUIRED_SHADOW_DIMENSIONS = Object.freeze([
28
+ "definition_revision",
29
+ "run_terminal_root",
30
+ "node_attempt_terminal",
31
+ "assignment_executor",
32
+ "message_handoff_custody",
33
+ "effect_receipt_reconciliation",
34
+ "artifact_provenance",
35
+ "budget_delta",
36
+ "workspace_commit_test_receipt",
37
+ ]);
38
+ export const GRAPH_REQUIRED_ROLLBACK_DRILLS = Object.freeze([
39
+ "shadow_to_legacy",
40
+ "canary_to_shadow",
41
+ "canonical_to_canary",
42
+ ]);
43
+
44
+ const COMMIT = /^[a-f0-9]{40,64}$/u;
45
+ const DIGEST = /^sha256:[a-f0-9]{64}$/u;
46
+ const ERROR_CODE = /^CC_[A-Z0-9_]{2,120}$/u;
47
+
48
+ function evidenceError(code, message, details = {}) {
49
+ const error = new Error(message);
50
+ error.name = "GraphProductionCutoverEvidenceError";
51
+ error.code = code;
52
+ Object.assign(error, details);
53
+ return error;
54
+ }
55
+
56
+ function text(value, field) {
57
+ const normalized = String(value || "").trim();
58
+ if (!normalized || Buffer.byteLength(normalized, "utf8") > 1024) {
59
+ throw evidenceError(
60
+ "CC_GRAPH_PRODUCTION_EVIDENCE_INVALID",
61
+ `${field} must be a non-empty string no larger than 1 KiB`,
62
+ { field },
63
+ );
64
+ }
65
+ return normalized;
66
+ }
67
+
68
+ function digest(value, field) {
69
+ const normalized = String(value || "");
70
+ if (!DIGEST.test(normalized)) {
71
+ throw evidenceError(
72
+ "CC_GRAPH_PRODUCTION_EVIDENCE_INVALID",
73
+ `${field} must be a sha256 digest`,
74
+ { field },
75
+ );
76
+ }
77
+ return normalized;
78
+ }
79
+
80
+ function commit(value, field = "commitSha") {
81
+ const normalized = String(value || "").toLowerCase();
82
+ if (!COMMIT.test(normalized)) {
83
+ throw evidenceError(
84
+ "CC_GRAPH_PRODUCTION_EVIDENCE_INVALID",
85
+ `${field} must be an exact commit SHA`,
86
+ { field },
87
+ );
88
+ }
89
+ return normalized;
90
+ }
91
+
92
+ function positive(value, field) {
93
+ const normalized = Number(value);
94
+ if (!Number.isSafeInteger(normalized) || normalized < 1) {
95
+ throw evidenceError(
96
+ "CC_GRAPH_PRODUCTION_EVIDENCE_INVALID",
97
+ `${field} must be a positive safe integer`,
98
+ { field, value },
99
+ );
100
+ }
101
+ return normalized;
102
+ }
103
+
104
+ function nonNegative(value, field) {
105
+ const normalized = Number(value);
106
+ if (!Number.isSafeInteger(normalized) || normalized < 0) {
107
+ throw evidenceError(
108
+ "CC_GRAPH_PRODUCTION_EVIDENCE_INVALID",
109
+ `${field} must be a non-negative safe integer`,
110
+ { field, value },
111
+ );
112
+ }
113
+ return normalized;
114
+ }
115
+
116
+ function atLeast(value, minimum, field) {
117
+ const normalized = nonNegative(value, field);
118
+ if (normalized < minimum) {
119
+ throw evidenceError(
120
+ "CC_GRAPH_PRODUCTION_EVIDENCE_INVALID",
121
+ `${field} must be at least ${minimum}`,
122
+ { field, value, minimum },
123
+ );
124
+ }
125
+ return normalized;
126
+ }
127
+
128
+ function zero(value, field) {
129
+ const normalized = nonNegative(value, field);
130
+ if (normalized !== 0) {
131
+ throw evidenceError(
132
+ "CC_GRAPH_PRODUCTION_GATE_FAILED",
133
+ `${field} must be zero`,
134
+ { field, value },
135
+ );
136
+ }
137
+ return 0;
138
+ }
139
+
140
+ function timestamp(value, field) {
141
+ const normalized = String(value || "");
142
+ const milliseconds = Date.parse(normalized);
143
+ if (
144
+ !Number.isFinite(milliseconds) ||
145
+ new Date(milliseconds).toISOString() !== normalized
146
+ ) {
147
+ throw evidenceError(
148
+ "CC_GRAPH_PRODUCTION_EVIDENCE_INVALID",
149
+ `${field} must be a canonical ISO timestamp`,
150
+ { field },
151
+ );
152
+ }
153
+ return { value: normalized, milliseconds };
154
+ }
155
+
156
+ function windowEvidence(input, field, { notBefore } = {}) {
157
+ const started = timestamp(input?.startedAt, `${field}.startedAt`);
158
+ const ended = timestamp(input?.endedAt, `${field}.endedAt`);
159
+ if (ended.milliseconds <= started.milliseconds) {
160
+ throw evidenceError(
161
+ "CC_GRAPH_PRODUCTION_OBSERVATION_INVALID",
162
+ `${field} must end after it starts`,
163
+ );
164
+ }
165
+ if (notBefore && started.milliseconds < Date.parse(notBefore)) {
166
+ throw evidenceError(
167
+ "CC_GRAPH_PRODUCTION_OBSERVATION_INVALID",
168
+ `${field} cannot start before canonical authority`,
169
+ { notBefore, startedAt: started.value },
170
+ );
171
+ }
172
+ return {
173
+ startedAt: started.value,
174
+ endedAt: ended.value,
175
+ durationMs: ended.milliseconds - started.milliseconds,
176
+ };
177
+ }
178
+
179
+ function clone(value) {
180
+ return value == null ? value : JSON.parse(JSON.stringify(value));
181
+ }
182
+
183
+ function sameMembers(actual, expected) {
184
+ return (
185
+ actual.length === expected.length &&
186
+ actual.every((value, index) => value === expected[index])
187
+ );
188
+ }
189
+
190
+ function compareCanonicalText(left, right) {
191
+ return left < right ? -1 : left > right ? 1 : 0;
192
+ }
193
+
194
+ function exactMembers(actual, expected, code, message, details = {}) {
195
+ const normalizedActual = [...actual].sort();
196
+ const normalizedExpected = [...expected].sort();
197
+ if (
198
+ new Set(normalizedActual).size !== normalizedActual.length ||
199
+ !sameMembers(normalizedActual, normalizedExpected)
200
+ ) {
201
+ throw evidenceError(code, message, {
202
+ ...details,
203
+ expected: normalizedExpected,
204
+ actual: normalizedActual,
205
+ });
206
+ }
207
+ }
208
+
209
+ function verifyDigest(input, normalized, domain, field = "evidenceDigest") {
210
+ const supplied = digest(input?.[field], field);
211
+ const unsigned = clone(normalized);
212
+ delete unsigned[field];
213
+ const expected = graphDigest(unsigned, domain);
214
+ if (supplied !== expected) {
215
+ throw evidenceError(
216
+ "CC_GRAPH_PRODUCTION_EVIDENCE_DIGEST_MISMATCH",
217
+ `${field} does not match the normalized evidence`,
218
+ { expectedEvidenceDigest: expected, actualEvidenceDigest: supplied },
219
+ );
220
+ }
221
+ return supplied;
222
+ }
223
+
224
+ function entryIdentity(input, expected, commitSha) {
225
+ const identity = {
226
+ surface: text(input?.surface, "surface"),
227
+ entryId: text(input?.entryId, "entryId"),
228
+ rolloutKey: text(input?.rolloutKey, "rolloutKey"),
229
+ cutoverStrategy: text(input?.cutoverStrategy, "cutoverStrategy"),
230
+ manifestDigest: digest(input?.manifestDigest, "manifestDigest"),
231
+ commitSha: commit(input?.commitSha),
232
+ };
233
+ const expectedManifestDigest = graphRuntimeEntryManifestDigest(
234
+ expected.manifest,
235
+ expected.surface.originSurface,
236
+ expected.entry.id,
237
+ );
238
+ if (
239
+ identity.surface !== expected.surface.originSurface ||
240
+ identity.entryId !== expected.entry.id ||
241
+ identity.rolloutKey !== expected.entry.rolloutKey ||
242
+ identity.cutoverStrategy !== expected.entry.cutoverStrategy ||
243
+ identity.manifestDigest !== expectedManifestDigest ||
244
+ identity.commitSha !== commitSha
245
+ ) {
246
+ throw evidenceError(
247
+ "CC_GRAPH_PRODUCTION_ENTRY_BINDING_MISMATCH",
248
+ "entry evidence does not match the manifest or exact commit",
249
+ {
250
+ surface: expected.surface.originSurface,
251
+ entryId: expected.entry.id,
252
+ },
253
+ );
254
+ }
255
+ return identity;
256
+ }
257
+
258
+ function normalizeShadow(input) {
259
+ const comparisons = Array.isArray(input?.comparisons)
260
+ ? input.comparisons.map((entry, index) => {
261
+ const dimension = text(
262
+ entry?.dimension,
263
+ `shadow.comparisons[${index}].dimension`,
264
+ );
265
+ if (entry?.status !== "equivalent") {
266
+ throw evidenceError(
267
+ "CC_GRAPH_PRODUCTION_SHADOW_DIVERGENCE",
268
+ `${dimension} is not equivalent`,
269
+ );
270
+ }
271
+ return {
272
+ dimension,
273
+ status: "equivalent",
274
+ sampleCount: positive(
275
+ entry?.sampleCount,
276
+ `shadow.comparisons[${index}].sampleCount`,
277
+ ),
278
+ evidenceDigest: digest(
279
+ entry?.evidenceDigest,
280
+ `shadow.comparisons[${index}].evidenceDigest`,
281
+ ),
282
+ };
283
+ })
284
+ : [];
285
+ comparisons.sort((left, right) =>
286
+ compareCanonicalText(left.dimension, right.dimension),
287
+ );
288
+ exactMembers(
289
+ comparisons.map((entry) => entry.dimension),
290
+ GRAPH_REQUIRED_SHADOW_DIMENSIONS,
291
+ "CC_GRAPH_PRODUCTION_SHADOW_COVERAGE_INCOMPLETE",
292
+ "shadow evidence must cover every semantic projection dimension",
293
+ );
294
+ return {
295
+ ...windowEvidence(input, "shadow"),
296
+ runCount: positive(input?.runCount, "shadow.runCount"),
297
+ divergenceCount: zero(input?.divergenceCount, "shadow.divergenceCount"),
298
+ unknownEffectCount: zero(
299
+ input?.unknownEffectCount,
300
+ "shadow.unknownEffectCount",
301
+ ),
302
+ realEffectInvocationCount: zero(
303
+ input?.realEffectInvocationCount,
304
+ "shadow.realEffectInvocationCount",
305
+ ),
306
+ comparisons,
307
+ evidenceDigest: digest(input?.evidenceDigest, "shadow.evidenceDigest"),
308
+ };
309
+ }
310
+
311
+ function normalizeCanary(input, commitSha) {
312
+ const platformJourneys = Array.isArray(input?.platformJourneys)
313
+ ? input.platformJourneys.map((entry, index) => {
314
+ const platform = String(entry?.platform || "").toLowerCase();
315
+ if (
316
+ !GRAPH_CUTOVER_REQUIRED_PLATFORMS.includes(platform) ||
317
+ commit(
318
+ entry?.commitSha,
319
+ `canary.platformJourneys[${index}].commitSha`,
320
+ ) !== commitSha ||
321
+ entry?.status !== "passed"
322
+ ) {
323
+ throw evidenceError(
324
+ "CC_GRAPH_PRODUCTION_CANARY_PLATFORM_FAILED",
325
+ "canary platform journey must pass on the exact commit",
326
+ { platform },
327
+ );
328
+ }
329
+ return {
330
+ platform,
331
+ commitSha,
332
+ status: "passed",
333
+ evidenceDigest: digest(
334
+ entry?.evidenceDigest,
335
+ `canary.platformJourneys[${index}].evidenceDigest`,
336
+ ),
337
+ };
338
+ })
339
+ : [];
340
+ platformJourneys.sort((left, right) =>
341
+ compareCanonicalText(left.platform, right.platform),
342
+ );
343
+ exactMembers(
344
+ platformJourneys.map((entry) => entry.platform),
345
+ GRAPH_CUTOVER_REQUIRED_PLATFORMS,
346
+ "CC_GRAPH_PRODUCTION_CANARY_PLATFORM_INCOMPLETE",
347
+ "canary evidence must cover every required platform",
348
+ );
349
+ return {
350
+ ...windowEvidence(input, "canary"),
351
+ internalRunCount: positive(
352
+ input?.internalRunCount,
353
+ "canary.internalRunCount",
354
+ ),
355
+ optInRunCount: positive(input?.optInRunCount, "canary.optInRunCount"),
356
+ defaultRunCount: positive(input?.defaultRunCount, "canary.defaultRunCount"),
357
+ failureCount: zero(input?.failureCount, "canary.failureCount"),
358
+ reconciliationCount: zero(
359
+ input?.reconciliationCount,
360
+ "canary.reconciliationCount",
361
+ ),
362
+ platformJourneys,
363
+ evidenceDigest: digest(input?.evidenceDigest, "canary.evidenceDigest"),
364
+ };
365
+ }
366
+
367
+ function normalizeRollback(input) {
368
+ const drills = Array.isArray(input?.drills)
369
+ ? input.drills.map((entry, index) => {
370
+ const transition = text(
371
+ entry?.transition,
372
+ `rollback.drills[${index}].transition`,
373
+ );
374
+ if (entry?.status !== "passed") {
375
+ throw evidenceError(
376
+ "CC_GRAPH_PRODUCTION_ROLLBACK_FAILED",
377
+ `${transition} rollback drill did not pass`,
378
+ );
379
+ }
380
+ if (
381
+ transition !== "shadow_to_legacy" &&
382
+ entry?.existingCanonicalRunsRetained !== true
383
+ ) {
384
+ throw evidenceError(
385
+ "CC_GRAPH_PRODUCTION_ROLLBACK_UNSAFE",
386
+ `${transition} must retain existing canonical run authority`,
387
+ );
388
+ }
389
+ return {
390
+ transition,
391
+ status: "passed",
392
+ activeDispatchCount: zero(
393
+ entry?.activeDispatchCount,
394
+ `rollback.drills[${index}].activeDispatchCount`,
395
+ ),
396
+ rpoLossCount: zero(
397
+ entry?.rpoLossCount,
398
+ `rollback.drills[${index}].rpoLossCount`,
399
+ ),
400
+ duplicateEffectCount: zero(
401
+ entry?.duplicateEffectCount,
402
+ `rollback.drills[${index}].duplicateEffectCount`,
403
+ ),
404
+ existingCanonicalRunsRetained:
405
+ transition === "shadow_to_legacy"
406
+ ? Boolean(entry?.existingCanonicalRunsRetained)
407
+ : true,
408
+ evidenceDigest: digest(
409
+ entry?.evidenceDigest,
410
+ `rollback.drills[${index}].evidenceDigest`,
411
+ ),
412
+ };
413
+ })
414
+ : [];
415
+ drills.sort((left, right) =>
416
+ compareCanonicalText(left.transition, right.transition),
417
+ );
418
+ exactMembers(
419
+ drills.map((entry) => entry.transition),
420
+ GRAPH_REQUIRED_ROLLBACK_DRILLS,
421
+ "CC_GRAPH_PRODUCTION_ROLLBACK_COVERAGE_INCOMPLETE",
422
+ "rollback evidence must cover every supported rollback boundary",
423
+ );
424
+ return {
425
+ drills,
426
+ evidenceDigest: digest(input?.evidenceDigest, "rollback.evidenceDigest"),
427
+ };
428
+ }
429
+
430
+ function normalizeMutationProbes(value, mutationFunctions, field) {
431
+ const probes = Array.isArray(value)
432
+ ? value.map((entry, index) => {
433
+ const mutationFunction = text(
434
+ entry?.mutationFunction,
435
+ `${field}[${index}].mutationFunction`,
436
+ );
437
+ const attemptCount = positive(
438
+ entry?.attemptCount,
439
+ `${field}[${index}].attemptCount`,
440
+ );
441
+ const blockedCount = positive(
442
+ entry?.blockedCount,
443
+ `${field}[${index}].blockedCount`,
444
+ );
445
+ if (
446
+ blockedCount !== attemptCount ||
447
+ !ERROR_CODE.test(entry?.errorCode)
448
+ ) {
449
+ throw evidenceError(
450
+ "CC_GRAPH_PRODUCTION_LEGACY_MUTATION_ALLOWED",
451
+ `${mutationFunction} was not consistently rejected with a stable error`,
452
+ );
453
+ }
454
+ return {
455
+ mutationFunction,
456
+ attemptCount,
457
+ blockedCount,
458
+ successCount: zero(
459
+ entry?.successCount,
460
+ `${field}[${index}].successCount`,
461
+ ),
462
+ errorCode: entry.errorCode,
463
+ evidenceDigest: digest(
464
+ entry?.evidenceDigest,
465
+ `${field}[${index}].evidenceDigest`,
466
+ ),
467
+ };
468
+ })
469
+ : [];
470
+ probes.sort((left, right) =>
471
+ compareCanonicalText(left.mutationFunction, right.mutationFunction),
472
+ );
473
+ exactMembers(
474
+ probes.map((entry) => entry.mutationFunction),
475
+ mutationFunctions,
476
+ "CC_GRAPH_PRODUCTION_LEGACY_MUTATION_COVERAGE_INCOMPLETE",
477
+ "legacy read-only evidence must probe every declared mutation",
478
+ );
479
+ return probes;
480
+ }
481
+
482
+ function normalizeMigratedWriterObservation(input, identity, entry, notBefore) {
483
+ if (input?.schema !== GRAPH_ENTRY_WRITER_OBSERVATION_SCHEMA) {
484
+ throw evidenceError(
485
+ "CC_GRAPH_PRODUCTION_WRITER_OBSERVATION_INVALID",
486
+ `writer observation schema must be ${GRAPH_ENTRY_WRITER_OBSERVATION_SCHEMA}`,
487
+ );
488
+ }
489
+ const observedIdentity = {
490
+ schema: GRAPH_ENTRY_WRITER_OBSERVATION_SCHEMA,
491
+ surface: text(input?.surface, "legacyReadOnly.surface"),
492
+ entryId: text(input?.entryId, "legacyReadOnly.entryId"),
493
+ rolloutKey: text(input?.rolloutKey, "legacyReadOnly.rolloutKey"),
494
+ manifestDigest: digest(
495
+ input?.manifestDigest,
496
+ "legacyReadOnly.manifestDigest",
497
+ ),
498
+ commitSha: commit(input?.commitSha, "legacyReadOnly.commitSha"),
499
+ };
500
+ for (const field of [
501
+ "surface",
502
+ "entryId",
503
+ "rolloutKey",
504
+ "manifestDigest",
505
+ "commitSha",
506
+ ]) {
507
+ if (observedIdentity[field] !== identity[field]) {
508
+ throw evidenceError(
509
+ "CC_GRAPH_PRODUCTION_ENTRY_BINDING_MISMATCH",
510
+ `legacy writer observation ${field} does not match its entry`,
511
+ );
512
+ }
513
+ }
514
+ const writerObservations = Array.isArray(input?.writerObservations)
515
+ ? input.writerObservations.map((observation, index) => ({
516
+ writerFile: text(
517
+ observation?.writerFile,
518
+ `legacyReadOnly.writerObservations[${index}].writerFile`,
519
+ ),
520
+ observationSampleCount: positive(
521
+ observation?.observationSampleCount,
522
+ `legacyReadOnly.writerObservations[${index}].observationSampleCount`,
523
+ ),
524
+ mutationSuccessCount: zero(
525
+ observation?.mutationSuccessCount,
526
+ `legacyReadOnly.writerObservations[${index}].mutationSuccessCount`,
527
+ ),
528
+ evidenceDigest: digest(
529
+ observation?.evidenceDigest,
530
+ `legacyReadOnly.writerObservations[${index}].evidenceDigest`,
531
+ ),
532
+ }))
533
+ : [];
534
+ writerObservations.sort((left, right) =>
535
+ compareCanonicalText(left.writerFile, right.writerFile),
536
+ );
537
+ exactMembers(
538
+ writerObservations.map((observation) => observation.writerFile),
539
+ entry.writerFiles,
540
+ "CC_GRAPH_PRODUCTION_WRITER_COVERAGE_INCOMPLETE",
541
+ "legacy writer observation must cover every writer file",
542
+ );
543
+ const normalized = {
544
+ ...observedIdentity,
545
+ ...windowEvidence(input, "legacyReadOnly", { notBefore }),
546
+ observationSampleCount: positive(
547
+ input?.observationSampleCount,
548
+ "legacyReadOnly.observationSampleCount",
549
+ ),
550
+ activeLegacyRunCount: zero(
551
+ input?.activeLegacyRunCount,
552
+ "legacyReadOnly.activeLegacyRunCount",
553
+ ),
554
+ legacyMutationSuccessCount: zero(
555
+ input?.legacyMutationSuccessCount,
556
+ "legacyReadOnly.legacyMutationSuccessCount",
557
+ ),
558
+ writerObservations,
559
+ mutationProbes: normalizeMutationProbes(
560
+ input?.mutationProbes,
561
+ entry.mutationFunctions,
562
+ "legacyReadOnly.mutationProbes",
563
+ ),
564
+ };
565
+ normalized.evidenceDigest = verifyDigest(
566
+ input,
567
+ normalized,
568
+ "cc.graph.entry-writer-observation/v1",
569
+ );
570
+ return normalized;
571
+ }
572
+
573
+ function retirementContract(entry) {
574
+ return normalizeGraphRetirementContract({
575
+ rolloutKey: entry.rolloutKey,
576
+ replacementEntrypoint: entry.replacementEntrypoint,
577
+ replacementEntryIds: entry.replacementEntryIds,
578
+ historicalReadFunctions: entry.historicalReadFunctions,
579
+ mutationFunctions: entry.mutationFunctions,
580
+ writerFiles: entry.writerFiles,
581
+ });
582
+ }
583
+
584
+ function normalizeFinalLedger(input, commitSha) {
585
+ const canonicalActivated = timestamp(
586
+ input?.canonicalActivatedAt,
587
+ "finalLedger.canonicalActivatedAt",
588
+ );
589
+ if (
590
+ input?.stage !== "legacy_read_only" ||
591
+ commit(input?.canonicalCommitSha, "finalLedger.canonicalCommitSha") !==
592
+ commitSha
593
+ ) {
594
+ throw evidenceError(
595
+ "CC_GRAPH_PRODUCTION_FINAL_STAGE_INVALID",
596
+ "final ledger must retain the exact canonical commit in legacy_read_only",
597
+ );
598
+ }
599
+ return {
600
+ stage: "legacy_read_only",
601
+ canonicalCommitSha: commitSha,
602
+ canonicalActivatedAt: canonicalActivated.value,
603
+ eventHead: digest(input?.eventHead, "finalLedger.eventHead"),
604
+ rollbackCount: atLeast(
605
+ input?.rollbackCount,
606
+ GRAPH_REQUIRED_ROLLBACK_DRILLS.length,
607
+ "finalLedger.rollbackCount",
608
+ ),
609
+ transitionCount: atLeast(
610
+ input?.transitionCount,
611
+ 10,
612
+ "finalLedger.transitionCount",
613
+ ),
614
+ evidenceDigest: digest(input?.evidenceDigest, "finalLedger.evidenceDigest"),
615
+ };
616
+ }
617
+
618
+ function normalizeEntry(input, expected, commitSha) {
619
+ const identity = entryIdentity(input, expected, commitSha);
620
+ exactMembers(
621
+ input?.stageSequence || [],
622
+ GRAPH_REQUIRED_CUTOVER_STAGES,
623
+ "CC_GRAPH_PRODUCTION_STAGE_COVERAGE_INCOMPLETE",
624
+ "entry evidence must cover every rollout stage",
625
+ { surface: identity.surface, entryId: identity.entryId },
626
+ );
627
+ if (
628
+ JSON.stringify(input.stageSequence) !==
629
+ JSON.stringify(GRAPH_REQUIRED_CUTOVER_STAGES)
630
+ ) {
631
+ throw evidenceError(
632
+ "CC_GRAPH_PRODUCTION_STAGE_ORDER_INVALID",
633
+ "rollout stages must occur in the canonical order",
634
+ { surface: identity.surface, entryId: identity.entryId },
635
+ );
636
+ }
637
+ const finalLedger = normalizeFinalLedger(input?.finalLedger, commitSha);
638
+ const shadow = normalizeShadow(input?.shadow);
639
+ const canary = normalizeCanary(input?.canary, commitSha);
640
+ if (
641
+ Date.parse(shadow.endedAt) > Date.parse(canary.startedAt) ||
642
+ Date.parse(canary.endedAt) > Date.parse(finalLedger.canonicalActivatedAt)
643
+ ) {
644
+ throw evidenceError(
645
+ "CC_GRAPH_PRODUCTION_STAGE_ORDER_INVALID",
646
+ "shadow, canary, and canonical activation evidence overlap or are out of order",
647
+ { surface: identity.surface, entryId: identity.entryId },
648
+ );
649
+ }
650
+ const normalized = {
651
+ ...identity,
652
+ stageSequence: [...GRAPH_REQUIRED_CUTOVER_STAGES],
653
+ shadow,
654
+ canary,
655
+ rollback: normalizeRollback(input?.rollback),
656
+ finalLedger,
657
+ };
658
+ if (expected.entry.cutoverStrategy === "retire") {
659
+ const contract = retirementContract(expected.entry);
660
+ normalized.retirementEvidence = normalizeGraphRetirementEvidence(
661
+ input?.retirementEvidence,
662
+ {
663
+ surface: identity.surface,
664
+ entryId: identity.entryId,
665
+ manifestDigest: identity.manifestDigest,
666
+ commitSha,
667
+ contract,
668
+ requiredPlatforms: GRAPH_CUTOVER_REQUIRED_PLATFORMS,
669
+ },
670
+ );
671
+ normalized.legacyReadOnly = normalizeGraphLegacyWriterObservation(
672
+ input?.legacyReadOnly,
673
+ {
674
+ surface: identity.surface,
675
+ entryId: identity.entryId,
676
+ manifestDigest: identity.manifestDigest,
677
+ commitSha,
678
+ contract,
679
+ notBefore: finalLedger.canonicalActivatedAt,
680
+ },
681
+ );
682
+ } else {
683
+ normalized.legacyReadOnly = normalizeMigratedWriterObservation(
684
+ input?.legacyReadOnly,
685
+ identity,
686
+ expected.entry,
687
+ finalLedger.canonicalActivatedAt,
688
+ );
689
+ }
690
+ normalized.evidenceDigest = verifyDigest(
691
+ input,
692
+ normalized,
693
+ "cc.graph.production-cutover-entry/v1",
694
+ );
695
+ return normalized;
696
+ }
697
+
698
+ function normalizeDisabledEntry(input, expected, commitSha) {
699
+ const manifestDigest = graphRuntimeEntryManifestDigest(
700
+ expected.manifest,
701
+ expected.surface.originSurface,
702
+ expected.entry.id,
703
+ );
704
+ const normalized = {
705
+ surface: text(input?.surface, "disabledEntry.surface"),
706
+ entryId: text(input?.entryId, "disabledEntry.entryId"),
707
+ rolloutKey: text(input?.rolloutKey, "disabledEntry.rolloutKey"),
708
+ manifestDigest: digest(
709
+ input?.manifestDigest,
710
+ "disabledEntry.manifestDigest",
711
+ ),
712
+ commitSha: commit(input?.commitSha, "disabledEntry.commitSha"),
713
+ cutoverStrategy: text(
714
+ input?.cutoverStrategy,
715
+ "disabledEntry.cutoverStrategy",
716
+ ),
717
+ runtimeDurability: text(
718
+ input?.runtimeDurability,
719
+ "disabledEntry.runtimeDurability",
720
+ ),
721
+ featureFlagDefault: text(
722
+ input?.featureFlagDefault,
723
+ "disabledEntry.featureFlagDefault",
724
+ ),
725
+ directEngineInvocationCount: zero(
726
+ input?.directEngineInvocationCount,
727
+ "disabledEntry.directEngineInvocationCount",
728
+ ),
729
+ durableAuthorityClaimCount: zero(
730
+ input?.durableAuthorityClaimCount,
731
+ "disabledEntry.durableAuthorityClaimCount",
732
+ ),
733
+ };
734
+ if (
735
+ normalized.surface !== expected.surface.originSurface ||
736
+ normalized.entryId !== expected.entry.id ||
737
+ normalized.rolloutKey !== expected.entry.rolloutKey ||
738
+ normalized.manifestDigest !== manifestDigest ||
739
+ normalized.commitSha !== commitSha ||
740
+ normalized.cutoverStrategy !== "disabled" ||
741
+ normalized.runtimeDurability !== "non_durable" ||
742
+ normalized.featureFlagDefault !== "disabled" ||
743
+ expected.surface.featureFlag.default !== "disabled"
744
+ ) {
745
+ throw evidenceError(
746
+ "CC_GRAPH_PRODUCTION_DISABLED_ENTRY_INVALID",
747
+ "disabled entry evidence must preserve truthful non-durable fail-closed claims",
748
+ { surface: expected.surface.originSurface, entryId: expected.entry.id },
749
+ );
750
+ }
751
+ normalized.evidenceDigest = verifyDigest(
752
+ input,
753
+ normalized,
754
+ "cc.graph.production-disabled-entry/v1",
755
+ );
756
+ return normalized;
757
+ }
758
+
759
+ function manifestEntries(manifest) {
760
+ return manifest.surfaces.flatMap((surface) =>
761
+ surface.entries.map((entry) => ({ manifest, surface, entry })),
762
+ );
763
+ }
764
+
765
+ function indexedEvidence(value, field) {
766
+ if (!Array.isArray(value)) {
767
+ throw evidenceError(
768
+ "CC_GRAPH_PRODUCTION_ENTRY_COVERAGE_INCOMPLETE",
769
+ `${field} must be an array`,
770
+ );
771
+ }
772
+ const map = new Map();
773
+ for (const entry of value) {
774
+ const key = `${entry?.surface}/${entry?.entryId}`;
775
+ if (map.has(key)) {
776
+ throw evidenceError(
777
+ "CC_GRAPH_PRODUCTION_ENTRY_COVERAGE_INCOMPLETE",
778
+ `${field} contains duplicate ${key}`,
779
+ );
780
+ }
781
+ map.set(key, entry);
782
+ }
783
+ return map;
784
+ }
785
+
786
+ export function graphProductionCutoverEvidenceDigest(value) {
787
+ const unsigned = clone(value);
788
+ delete unsigned.evidenceDigest;
789
+ return graphDigest(unsigned, "cc.graph.production-cutover-evidence/v1");
790
+ }
791
+
792
+ export function normalizeGraphProductionCutoverEvidence(
793
+ input,
794
+ {
795
+ manifest = loadGraphRuntimeSurfaceManifest(),
796
+ expectedCommitSha = undefined,
797
+ expectedRepository = undefined,
798
+ expectedEnvironment = undefined,
799
+ expectedWorkflowRunId = undefined,
800
+ expectedWorkflowRunAttempt = undefined,
801
+ } = {},
802
+ ) {
803
+ if (input?.schema !== GRAPH_PRODUCTION_CUTOVER_EVIDENCE_SCHEMA) {
804
+ throw evidenceError(
805
+ "CC_GRAPH_PRODUCTION_EVIDENCE_INVALID",
806
+ `evidence schema must be ${GRAPH_PRODUCTION_CUTOVER_EVIDENCE_SCHEMA}`,
807
+ );
808
+ }
809
+ const commitSha = commit(input?.commitSha);
810
+ if (expectedCommitSha && commitSha !== commit(expectedCommitSha)) {
811
+ throw evidenceError(
812
+ "CC_GRAPH_PRODUCTION_SHA_MISMATCH",
813
+ "production cutover evidence does not match the expected commit",
814
+ );
815
+ }
816
+ const provenance = {
817
+ repository: text(input?.provenance?.repository, "provenance.repository"),
818
+ environment: text(input?.provenance?.environment, "provenance.environment"),
819
+ workflowRunId: positive(
820
+ input?.provenance?.workflowRunId,
821
+ "provenance.workflowRunId",
822
+ ),
823
+ workflowRunAttempt: positive(
824
+ input?.provenance?.workflowRunAttempt,
825
+ "provenance.workflowRunAttempt",
826
+ ),
827
+ oidcAttestationDigest: digest(
828
+ input?.provenance?.oidcAttestationDigest,
829
+ "provenance.oidcAttestationDigest",
830
+ ),
831
+ };
832
+ if (
833
+ (expectedRepository && provenance.repository !== expectedRepository) ||
834
+ (expectedEnvironment && provenance.environment !== expectedEnvironment) ||
835
+ (expectedWorkflowRunId !== undefined &&
836
+ provenance.workflowRunId !== Number(expectedWorkflowRunId)) ||
837
+ (expectedWorkflowRunAttempt !== undefined &&
838
+ provenance.workflowRunAttempt !== Number(expectedWorkflowRunAttempt))
839
+ ) {
840
+ throw evidenceError(
841
+ "CC_GRAPH_PRODUCTION_PROVENANCE_MISMATCH",
842
+ "production evidence provenance does not match the protected producer",
843
+ {
844
+ expectedRepository,
845
+ expectedEnvironment,
846
+ expectedWorkflowRunId:
847
+ expectedWorkflowRunId === undefined
848
+ ? undefined
849
+ : Number(expectedWorkflowRunId),
850
+ expectedWorkflowRunAttempt:
851
+ expectedWorkflowRunAttempt === undefined
852
+ ? undefined
853
+ : Number(expectedWorkflowRunAttempt),
854
+ actual: provenance,
855
+ },
856
+ );
857
+ }
858
+ const allEntries = manifestEntries(manifest);
859
+ const durable = allEntries.filter(
860
+ ({ entry }) => entry.cutoverStrategy !== "disabled",
861
+ );
862
+ const disabled = allEntries.filter(
863
+ ({ entry }) => entry.cutoverStrategy === "disabled",
864
+ );
865
+ const providedEntries = indexedEvidence(input?.entries, "entries");
866
+ const providedDisabled = indexedEvidence(
867
+ input?.disabledEntries,
868
+ "disabledEntries",
869
+ );
870
+ exactMembers(
871
+ [...providedEntries.keys()],
872
+ durable.map(({ surface, entry }) => `${surface.originSurface}/${entry.id}`),
873
+ "CC_GRAPH_PRODUCTION_ENTRY_COVERAGE_INCOMPLETE",
874
+ "production cutover evidence must cover every non-disabled entry",
875
+ );
876
+ exactMembers(
877
+ [...providedDisabled.keys()],
878
+ disabled.map(
879
+ ({ surface, entry }) => `${surface.originSurface}/${entry.id}`,
880
+ ),
881
+ "CC_GRAPH_PRODUCTION_DISABLED_COVERAGE_INCOMPLETE",
882
+ "production cutover evidence must cover every disabled entry",
883
+ );
884
+ const entries = durable
885
+ .map((expected) =>
886
+ normalizeEntry(
887
+ providedEntries.get(
888
+ `${expected.surface.originSurface}/${expected.entry.id}`,
889
+ ),
890
+ expected,
891
+ commitSha,
892
+ ),
893
+ )
894
+ .sort((left, right) =>
895
+ compareCanonicalText(
896
+ `${left.surface}/${left.entryId}`,
897
+ `${right.surface}/${right.entryId}`,
898
+ ),
899
+ );
900
+ const disabledEntries = disabled
901
+ .map((expected) =>
902
+ normalizeDisabledEntry(
903
+ providedDisabled.get(
904
+ `${expected.surface.originSurface}/${expected.entry.id}`,
905
+ ),
906
+ expected,
907
+ commitSha,
908
+ ),
909
+ )
910
+ .sort((left, right) =>
911
+ compareCanonicalText(
912
+ `${left.surface}/${left.entryId}`,
913
+ `${right.surface}/${right.entryId}`,
914
+ ),
915
+ );
916
+ const observedAt = timestamp(input?.observedAt, "observedAt");
917
+ const latestEntryObservation = Math.max(
918
+ ...entries.map((entry) => Date.parse(entry.legacyReadOnly.endedAt)),
919
+ );
920
+ if (observedAt.milliseconds < latestEntryObservation) {
921
+ throw evidenceError(
922
+ "CC_GRAPH_PRODUCTION_OBSERVATION_INVALID",
923
+ "aggregate observedAt cannot predate an entry observation window",
924
+ );
925
+ }
926
+ const normalized = {
927
+ schema: GRAPH_PRODUCTION_CUTOVER_EVIDENCE_SCHEMA,
928
+ commitSha,
929
+ observedAt: observedAt.value,
930
+ provenance,
931
+ entries,
932
+ disabledEntries,
933
+ };
934
+ normalized.evidenceDigest = verifyDigest(
935
+ input,
936
+ normalized,
937
+ "cc.graph.production-cutover-evidence/v1",
938
+ );
939
+ return Object.freeze(normalized);
940
+ }
941
+
942
+ export function createGraphProductionCutoverReceipt(input, options = {}) {
943
+ const evidence = normalizeGraphProductionCutoverEvidence(input, options);
944
+ const surfaceNames = new Set([
945
+ ...evidence.entries.map((entry) => entry.surface),
946
+ ...evidence.disabledEntries.map((entry) => entry.surface),
947
+ ]);
948
+ const receipt = {
949
+ schema: GRAPH_PRODUCTION_CUTOVER_RECEIPT_SCHEMA,
950
+ commitSha: evidence.commitSha,
951
+ evidenceDigest: evidence.evidenceDigest,
952
+ status: "passed",
953
+ surfaceCount: surfaceNames.size,
954
+ durableEntryCount: evidence.entries.length,
955
+ migratedEntryCount: evidence.entries.filter(
956
+ (entry) => entry.cutoverStrategy === "migrate",
957
+ ).length,
958
+ retiredEntryCount: evidence.entries.filter(
959
+ (entry) => entry.cutoverStrategy === "retire",
960
+ ).length,
961
+ disabledEntryCount: evidence.disabledEntries.length,
962
+ projectionDimensionCount: GRAPH_REQUIRED_SHADOW_DIMENSIONS.length,
963
+ rollbackDrillCount:
964
+ evidence.entries.length * GRAPH_REQUIRED_ROLLBACK_DRILLS.length,
965
+ legacyMutationCount: evidence.entries.reduce(
966
+ (total, entry) => total + entry.legacyReadOnly.mutationProbes.length,
967
+ 0,
968
+ ),
969
+ provenance: clone(evidence.provenance),
970
+ };
971
+ receipt.receiptDigest = graphDigest(
972
+ receipt,
973
+ "cc.graph.production-cutover-receipt/v1",
974
+ );
975
+ return Object.freeze(receipt);
976
+ }