chainlesschain 0.166.6 → 0.166.7

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 (189) hide show
  1. package/package.json +3 -1
  2. package/src/assets/web-panel/assets/{AIOps-CVnFjTfG.js → AIOps-BlrmQxa2.js} +1 -1
  3. package/src/assets/web-panel/assets/{ActionButton-BxkwuuQC.js → ActionButton-zyRyLITg.js} +1 -1
  4. package/src/assets/web-panel/assets/{Analytics-C2670q6z.js → Analytics-DwGz7OGN.js} +3 -3
  5. package/src/assets/web-panel/assets/{AppLayout-CU_rylW6.js → AppLayout-Dc-nj7Zd.js} +5 -5
  6. package/src/assets/web-panel/assets/{Artifacts-DGJmYqnj.js → Artifacts-7Hu3O73p.js} +1 -1
  7. package/src/assets/web-panel/assets/{Audit-BZOQbINJ.js → Audit-Bnoe3-fR.js} +1 -1
  8. package/src/assets/web-panel/assets/{BackgroundAgents-DghB4QwF.js → BackgroundAgents-BqYFrDKZ.js} +1 -1
  9. package/src/assets/web-panel/assets/{Backup-l0mwPbta.js → Backup-B3hwW1nz.js} +1 -1
  10. package/src/assets/web-panel/assets/{BaseInput-Bv3WSZOh.js → BaseInput-XFVxLA4w.js} +1 -1
  11. package/src/assets/web-panel/assets/{Chat-s88nBdvt.js → Chat-CWRDNw7e.js} +5 -5
  12. package/src/assets/web-panel/assets/{ChatBubbleRenderer-Z8CBuB1a.js → ChatBubbleRenderer-Bf30VM8G.js} +1 -1
  13. package/src/assets/web-panel/assets/{Checkbox-B5icasEs.js → Checkbox-kFeeBgac.js} +1 -1
  14. package/src/assets/web-panel/assets/{Codegen-CsUFqNud.js → Codegen-CCxp8xoI.js} +1 -1
  15. package/src/assets/web-panel/assets/{Col-DlBtClaW.js → Col-BLKZalle.js} +1 -1
  16. package/src/assets/web-panel/assets/{Community-xRn1IdLB.js → Community-DD0eWCez.js} +1 -1
  17. package/src/assets/web-panel/assets/{Compact-naIr8a_o.js → Compact-CHqHWdy0.js} +1 -1
  18. package/src/assets/web-panel/assets/{Compliance-Dp9esfFE.js → Compliance-Bw54uPiK.js} +1 -1
  19. package/src/assets/web-panel/assets/{Cowork-DQlMYJB-.js → Cowork-DZ13zKNJ.js} +4 -4
  20. package/src/assets/web-panel/assets/{Cron-Cd0oNLzJ.js → Cron-Dlj5Jevg.js} +2 -2
  21. package/src/assets/web-panel/assets/{Crosschain-BtDIx3XX.js → Crosschain-Nvz0aR0j.js} +1 -1
  22. package/src/assets/web-panel/assets/{DID-BOlpzEwg.js → DID-Ct89eUyY.js} +2 -2
  23. package/src/assets/web-panel/assets/{Dashboard-DV15Axk1.js → Dashboard-BHUH5IAe.js} +2 -2
  24. package/src/assets/web-panel/assets/{Dropdown-COXrewN1.js → Dropdown-BhZG-PO7.js} +1 -1
  25. package/src/assets/web-panel/assets/{EmailListRenderer-EjIt0Cm7.js → EmailListRenderer-ae4RVwwJ.js} +1 -1
  26. package/src/assets/web-panel/assets/{FamilyGuardDashboard-DJlhwb-R.js → FamilyGuardDashboard-AOcxLfLr.js} +1 -1
  27. package/src/assets/web-panel/assets/{Federation-BMiFl82k.js → Federation-BK2PJQOF.js} +1 -1
  28. package/src/assets/web-panel/assets/{FormItemContext-BUcDQyxn.js → FormItemContext-C2n3xO2_.js} +1 -1
  29. package/src/assets/web-panel/assets/{GenericCardRenderer-C25uFxvK.js → GenericCardRenderer-DcyUVFZq.js} +1 -1
  30. package/src/assets/web-panel/assets/{Git-D20uaEUU.js → Git-tBae9Lt8.js} +2 -2
  31. package/src/assets/web-panel/assets/{Governance-CQxYJjCX.js → Governance-DmNR3AP4.js} +1 -1
  32. package/src/assets/web-panel/assets/{Inference-m-sczpS5.js → Inference-CoseEigi.js} +1 -1
  33. package/src/assets/web-panel/assets/{KnowledgeGraph-CBsIlU0S.js → KnowledgeGraph-Bdjd1sxr.js} +1 -1
  34. package/src/assets/web-panel/assets/{Logs-D0-5bqeu.js → Logs-CtlkKIcn.js} +2 -2
  35. package/src/assets/web-panel/assets/{MarkdownRenderer-BwH414Ms.js → MarkdownRenderer-CZsTBEQE.js} +1 -1
  36. package/src/assets/web-panel/assets/{Marketplace-CwDGFu2Q.js → Marketplace-BTYb7nEx.js} +1 -1
  37. package/src/assets/web-panel/assets/{McpTools-7FxJ3omT.js → McpTools-D0iUdW3P.js} +2 -2
  38. package/src/assets/web-panel/assets/{Memory-DOVyb59U.js → Memory--gwI7w4Y.js} +2 -2
  39. package/src/assets/web-panel/assets/{MobileBridge-DZXl-Hje.js → MobileBridge-JmFBi24T.js} +1 -1
  40. package/src/assets/web-panel/assets/{MobileProjects-DlZ8tRXt.js → MobileProjects-BK01cQRx.js} +1 -1
  41. package/src/assets/web-panel/assets/{Mtc-D7Pjx1s_.js → Mtc-u7avzyoO.js} +5 -5
  42. package/src/assets/web-panel/assets/{MtcAudit-BND7lX8p.js → MtcAudit-gT3z5fo_.js} +2 -2
  43. package/src/assets/web-panel/assets/{Multisig-BknS7B-p.js → Multisig-BIqZTz5n.js} +3 -3
  44. package/src/assets/web-panel/assets/{NLProgramming-7C2-KPHt.js → NLProgramming-DdhVfXYq.js} +1 -1
  45. package/src/assets/web-panel/assets/{Notes-DNWdTndN.js → Notes-CBlyUmY3.js} +4 -4
  46. package/src/assets/web-panel/assets/{NotificationSettings-DCm4KcJZ.js → NotificationSettings-DSyI26N3.js} +1 -1
  47. package/src/assets/web-panel/assets/{OrderTableRenderer-DyVwwjJx.js → OrderTableRenderer-B7IEuNz4.js} +1 -1
  48. package/src/assets/web-panel/assets/{Organization-Dl2O_qJ9.js → Organization-ozhi4kGS.js} +4 -4
  49. package/src/assets/web-panel/assets/{Overflow-BOFCvk9Q.js → Overflow-CBy57RVM.js} +1 -1
  50. package/src/assets/web-panel/assets/{P2P-DgdVowY1.js → P2P-CA-ZRv77.js} +2 -2
  51. package/src/assets/web-panel/assets/PdhVaultBrowser-DOHc9zm8.js +7 -0
  52. package/src/assets/web-panel/assets/{Permissions-BHNEUUwa.js → Permissions-DiP632GP.js} +4 -4
  53. package/src/assets/web-panel/assets/{PersonalDataHub-CaZbrOdz.js → PersonalDataHub-AnU8O-SS.js} +5 -5
  54. package/src/assets/web-panel/assets/{Pipeline-DJDnoXmE.js → Pipeline-DDibOUaP.js} +1 -1
  55. package/src/assets/web-panel/assets/{Privacy-CBQr2eX8.js → Privacy-D421t36T.js} +1 -1
  56. package/src/assets/web-panel/assets/{ProjectInit-Yghqa06z.js → ProjectInit-DR879ZPJ.js} +2 -2
  57. package/src/assets/web-panel/assets/{ProjectSettings-HBAtJi8U.js → ProjectSettings-Ded8o6aY.js} +2 -2
  58. package/src/assets/web-panel/assets/{Projects-DiCGnMjz.js → Projects-BHPxiucB.js} +1 -1
  59. package/src/assets/web-panel/assets/{Providers-uJTz77GI.js → Providers-Boivuu-E.js} +1 -1
  60. package/src/assets/web-panel/assets/{QrScannerModal-k3M5ouTp.js → QrScannerModal-xKD_ntKp.js} +1 -1
  61. package/src/assets/web-panel/assets/{QuickAsk-CMji45PH.js → QuickAsk-D3y_A6FV.js} +1 -1
  62. package/src/assets/web-panel/assets/{Recommend-DyoZYPls.js → Recommend-BjKM-YC2.js} +1 -1
  63. package/src/assets/web-panel/assets/{RemoteSession-s1gR38IO.js → RemoteSession-CgqKMXG4.js} +2 -2
  64. package/src/assets/web-panel/assets/{Reputation-D4hIs4vV.js → Reputation-Ci2GjH5p.js} +1 -1
  65. package/src/assets/web-panel/assets/{Row-mzE-ElTg.js → Row-B0-T2BSh.js} +1 -1
  66. package/src/assets/web-panel/assets/{RssFeed-cg7J3hI6.js → RssFeed-Bet-ur-r.js} +3 -3
  67. package/src/assets/web-panel/assets/{Search-6M0TkGSZ.js → Search-DWtRAS2R.js} +1 -1
  68. package/src/assets/web-panel/assets/{Security-B1GQ67qh.js → Security-BDfu2Qn6.js} +4 -4
  69. package/src/assets/web-panel/assets/{Services-D_RwU_5S.js → Services-CvJmvSMM.js} +2 -2
  70. package/src/assets/web-panel/assets/{Skeleton-TZjk-dsW.js → Skeleton-CEbrKpdC.js} +1 -1
  71. package/src/assets/web-panel/assets/{Skills-CG6FcaCV.js → Skills-CjVeiETf.js} +1 -1
  72. package/src/assets/web-panel/assets/{Sla-BI190QDv.js → Sla-Dibypxum.js} +1 -1
  73. package/src/assets/web-panel/assets/{SpeechSettings-BWmxF2Wu.js → SpeechSettings-XflYXp7a.js} +1 -1
  74. package/src/assets/web-panel/assets/{SyncSettings-CwPiGw6L.js → SyncSettings-ro1Ivx1E.js} +2 -2
  75. package/src/assets/web-panel/assets/{Tasks-BIXgCeSk.js → Tasks-CHq1qFbo.js} +1 -1
  76. package/src/assets/web-panel/assets/{Templates-CQFgGCoK.js → Templates-CRnUZxtZ.js} +1 -1
  77. package/src/assets/web-panel/assets/{Tenant-DAelEdkL.js → Tenant-jUX72JCA.js} +1 -1
  78. package/src/assets/web-panel/assets/{Terminal-BbqWK6qh.js → Terminal-BBhAs7pt.js} +2 -2
  79. package/src/assets/web-panel/assets/{TimelineRenderer-cmG3zvY5.js → TimelineRenderer-CD5M3RGp.js} +1 -1
  80. package/src/assets/web-panel/assets/{Tokens-C8444NKU.js → Tokens-sah3v0gu.js} +1 -1
  81. package/src/assets/web-panel/assets/{Trigger-C1Rpnz0I.js → Trigger-BNT3Uz3s.js} +1 -1
  82. package/src/assets/web-panel/assets/{Trust-u4koPiFD.js → Trust-BytMGPMo.js} +1 -1
  83. package/src/assets/web-panel/assets/{UkeySign-oPWKn80Z.js → UkeySign-BkYVMRQs.js} +1 -1
  84. package/src/assets/web-panel/assets/{VideoEditing-D0CUUY_5.js → VideoEditing-psvb5Q7Y.js} +1 -1
  85. package/src/assets/web-panel/assets/{Wallet-fu9pIAbt.js → Wallet-PFQTW0MH.js} +4 -4
  86. package/src/assets/web-panel/assets/{WebAuthn-Bc5ciuOx.js → WebAuthn-DgO-OwRT.js} +5 -5
  87. package/src/assets/web-panel/assets/{WorkflowEditor-BCin4_4N.js → WorkflowEditor-DL2l9VNl.js} +1 -1
  88. package/src/assets/web-panel/assets/{chat-B2jbM-XN.js → chat-C84akEzN.js} +1 -1
  89. package/src/assets/web-panel/assets/{colors-CUVoUQ_2.js → colors-DzTWuQKP.js} +1 -1
  90. package/src/assets/web-panel/assets/{compact-item-DgOGpkRH.js → compact-item-CR9LJQFv.js} +1 -1
  91. package/src/assets/web-panel/assets/{createContext-DIxU6eKI.js → createContext-CTTov5Kn.js} +1 -1
  92. package/src/assets/web-panel/assets/devWarning-BOhvmlpc.js +1 -0
  93. package/src/assets/web-panel/assets/{hasIn-DoKRKMav.js → hasIn-DALj523o.js} +1 -1
  94. package/src/assets/web-panel/assets/{index-i5MB455u.js → index-3-XJoYgX.js} +1 -1
  95. package/src/assets/web-panel/assets/{index-CLdpHe6w.js → index-7ux_XohH.js} +1 -1
  96. package/src/assets/web-panel/assets/{index-B3F_p8NA.js → index-AXnERsyV.js} +1 -1
  97. package/src/assets/web-panel/assets/{index-D2yo6ast.js → index-B2qpjU-D.js} +1 -1
  98. package/src/assets/web-panel/assets/{index-CWbL1-N0.js → index-B6QOJbSn.js} +1 -1
  99. package/src/assets/web-panel/assets/{index-DtyAuGOm.js → index-BDyt57zL.js} +1 -1
  100. package/src/assets/web-panel/assets/{index-DpTFx8Xx.js → index-BHRXKiU1.js} +1 -1
  101. package/src/assets/web-panel/assets/{index-CF80xxPG.js → index-BKNIzw0U.js} +1 -1
  102. package/src/assets/web-panel/assets/{index-BRMEO2Tl.js → index-BNaQt49r.js} +1 -1
  103. package/src/assets/web-panel/assets/{index-ByjrulIZ.js → index-Be6Os5S8.js} +1 -1
  104. package/src/assets/web-panel/assets/{index-BCLeNqIL.js → index-C5eeMl_2.js} +1 -1
  105. package/src/assets/web-panel/assets/{index-DJMHnbmu.js → index-CNmTDXmf.js} +1 -1
  106. package/src/assets/web-panel/assets/{index-2flsPFIX.js → index-CXTEpfGe.js} +1 -1
  107. package/src/assets/web-panel/assets/{index-BwYofQJg.js → index-CXaCRXN4.js} +1 -1
  108. package/src/assets/web-panel/assets/{index-B5ySiGYy.js → index-CZhhFQi9.js} +1 -1
  109. package/src/assets/web-panel/assets/{index-CeWSw-z5.js → index-Cef0n4k_.js} +1 -1
  110. package/src/assets/web-panel/assets/{index-C846lPGD.js → index-CjkPDSX4.js} +1 -1
  111. package/src/assets/web-panel/assets/{index-C9TsO9T9.js → index-CmELknPe.js} +1 -1
  112. package/src/assets/web-panel/assets/{index-CqlwIjJx.js → index-CnxjXIsK.js} +1 -1
  113. package/src/assets/web-panel/assets/{index-Co4lDEk-.js → index-CqUgXYIQ.js} +1 -1
  114. package/src/assets/web-panel/assets/{index-Cj-us-D_.js → index-CunGDV0g.js} +1 -1
  115. package/src/assets/web-panel/assets/{index-BFUprhNd.js → index-D0MqUnKO.js} +1 -1
  116. package/src/assets/web-panel/assets/{index-DGTZdZOE.js → index-D9ScRFxz.js} +3 -3
  117. package/src/assets/web-panel/assets/{index-C88m9lsi.js → index-DBIPnoTj.js} +1 -1
  118. package/src/assets/web-panel/assets/index-DDyMJexx.js +1 -0
  119. package/src/assets/web-panel/assets/{index-BXb-Smmm.js → index-DH6OG-ci.js} +1 -1
  120. package/src/assets/web-panel/assets/{index-eTWPaeBI.js → index-DZER6Lj3.js} +1 -1
  121. package/src/assets/web-panel/assets/{index-Cskz--fd.js → index-D_Bg0LdK.js} +1 -1
  122. package/src/assets/web-panel/assets/{index-D-ufbAJB.js → index-Dah8M4l2.js} +1 -1
  123. package/src/assets/web-panel/assets/{index-DHugFl3v.js → index-DjE_ew7U.js} +1 -1
  124. package/src/assets/web-panel/assets/{index-B9mSYovv.js → index-DtW6Zr_i.js} +1 -1
  125. package/src/assets/web-panel/assets/{index-Ds8r3Fzb.js → index-DxdzSljS.js} +1 -1
  126. package/src/assets/web-panel/assets/{index-CPXdBnGi.js → index-QW0EXx14.js} +1 -1
  127. package/src/assets/web-panel/assets/{index-B-f6l4Vm.js → index-W9axN_Ce.js} +1 -1
  128. package/src/assets/web-panel/assets/{index-CpvyOt5Z.js → index-_8vetPbU.js} +1 -1
  129. package/src/assets/web-panel/assets/index-btiojFYn.js +1 -0
  130. package/src/assets/web-panel/assets/{index-eYxUASSG.js → index-eJcFleIV.js} +1 -1
  131. package/src/assets/web-panel/assets/{index-be8SonCh.js → index-hzzTITR9.js} +1 -1
  132. package/src/assets/web-panel/assets/{index-BtSPhEyC.js → index-s4CFyn-n.js} +1 -1
  133. package/src/assets/web-panel/assets/{initDefaultProps-B4chl7iR.js → initDefaultProps-JuW8mFj7.js} +1 -1
  134. package/src/assets/web-panel/assets/{motion-DruN9h96.js → motion-Cwb0HBve.js} +1 -1
  135. package/src/assets/web-panel/assets/{move-Cl0UtCgU.js → move-DHokzMgu.js} +1 -1
  136. package/src/assets/web-panel/assets/{mtc-parser-FX2ttw70.js → mtc-parser-DX31y6QS.js} +1 -1
  137. package/src/assets/web-panel/assets/{omit-BDCneY46.js → omit-BCIUYDA6.js} +1 -1
  138. package/src/assets/web-panel/assets/{pickAttrs-DycsEg8b.js → pickAttrs-DMCj4EoZ.js} +1 -1
  139. package/src/assets/web-panel/assets/{placementArrow-Bc-dBuMv.js → placementArrow-Dy5K-u1s.js} +1 -1
  140. package/src/assets/web-panel/assets/{responsiveObserve--Wwaf8lu.js → responsiveObserve-DkGeZu54.js} +1 -1
  141. package/src/assets/web-panel/assets/{slide-vDmHQqHH.js → slide-Dc5dyJdg.js} +1 -1
  142. package/src/assets/web-panel/assets/{statusUtils-9c52yv3G.js → statusUtils-DIUBjOaC.js} +1 -1
  143. package/src/assets/web-panel/assets/{styleChecker-BT4BiO6_.js → styleChecker-DmyEOxm5.js} +1 -1
  144. package/src/assets/web-panel/assets/{useFlexGapSupport-C7XxWPcP.js → useFlexGapSupport-FRhakk1e.js} +1 -1
  145. package/src/assets/web-panel/assets/{useFs-BHoMqh4i.js → useFs-clXyV1WQ.js} +1 -1
  146. package/src/assets/web-panel/assets/{usePersonalDataHub-BOLxvsAG.js → usePersonalDataHub-UF6DG41z.js} +1 -1
  147. package/src/assets/web-panel/assets/{vnode-CA7poeeP.js → vnode-CoCt95G7.js} +1 -1
  148. package/src/assets/web-panel/assets/{zoom-B_sAn-co.js → zoom-yVWt-ENm.js} +1 -1
  149. package/src/assets/web-panel/index.html +1 -1
  150. package/src/commands/graph.js +215 -5
  151. package/src/commands/team-distributed.js +342 -2
  152. package/src/commands/team.js +169 -7
  153. package/src/data/changelog.json +13 -0
  154. package/src/generated/cc-agent-protocol.schema.json +6 -1
  155. package/src/lib/agent-team/task-lease.js +315 -0
  156. package/src/lib/agent-team/team-agent-stream.js +16 -0
  157. package/src/lib/agent-team/team-distributed-graph-bridge.js +257 -0
  158. package/src/lib/agent-team/team-distributed-graph-writer.js +523 -0
  159. package/src/lib/agent-team/team-distributed-queue.js +251 -0
  160. package/src/lib/agent-team/team-graph-runtime-adapter.js +1918 -0
  161. package/src/lib/agent-team/team-runner.js +225 -2
  162. package/src/lib/app-server/graph-runtime.js +754 -0
  163. package/src/lib/app-server/rollout-store.js +32 -0
  164. package/src/lib/app-server/server.js +140 -0
  165. package/src/lib/autonomous-agent.js +11 -0
  166. package/src/lib/cowork-graph-authority-adapter.js +455 -0
  167. package/src/lib/dynamic-workflow-facade.js +83 -16
  168. package/src/lib/graph-kernel/adapters.js +93 -20
  169. package/src/lib/graph-kernel/authority.js +324 -0
  170. package/src/lib/graph-kernel/compiler.js +1 -8
  171. package/src/lib/graph-kernel/cutover-authority-resolver.js +219 -0
  172. package/src/lib/graph-kernel/cutover-ledger.js +650 -0
  173. package/src/lib/graph-kernel/event-store.js +15 -1
  174. package/src/lib/graph-kernel/graph-runtime-surfaces.json +1427 -0
  175. package/src/lib/graph-kernel/index.js +4 -0
  176. package/src/lib/graph-kernel/migration-saga.js +309 -0
  177. package/src/lib/graph-kernel/runtime-surface-manifest.js +481 -0
  178. package/src/lib/graph-kernel/runtime.js +661 -24
  179. package/src/lib/graph-kernel/store-cutover-evidence.js +354 -0
  180. package/src/lib/graph-kernel/trace-reducer.js +6 -0
  181. package/src/lib/graph-kernel/trigger-adapter.js +60 -3
  182. package/src/lib/legacy-runtime-authority.js +160 -0
  183. package/src/lib/orchestrator.js +28 -0
  184. package/src/lib/scheduler-kernel/graph-authority-adapter.js +512 -0
  185. package/src/lib/scheduler-kernel/runtime.js +75 -2
  186. package/src/assets/web-panel/assets/PdhVaultBrowser-BiaZfQIM.js +0 -7
  187. package/src/assets/web-panel/assets/devWarning-DiSlgCC4.js +0 -1
  188. package/src/assets/web-panel/assets/index-9WIQEHvv.js +0 -1
  189. package/src/assets/web-panel/assets/index-B7mDobAl.js +0 -1
@@ -0,0 +1,650 @@
1
+ import { createHash } from "node:crypto";
2
+ import { JsonlRolloutStore } from "../app-server/rollout-store.js";
3
+ import { assertGraphCutoverTransition } from "./authority.js";
4
+ import { graphDigest } from "./compiler.js";
5
+ import { graphStoreEvidenceDigest } from "./store-cutover-evidence.js";
6
+
7
+ export const GRAPH_CUTOVER_LEDGER_SCHEMA =
8
+ "chainlesschain.graph-cutover-ledger/v1";
9
+ export const GRAPH_CUTOVER_REQUIRED_PLATFORMS = Object.freeze([
10
+ "linux",
11
+ "windows",
12
+ "macos",
13
+ ]);
14
+
15
+ const IDENTIFIER = /^[A-Za-z0-9][A-Za-z0-9._:/-]{0,159}$/u;
16
+ const DIGEST = /^sha256:[a-f0-9]{64}$/u;
17
+ const COMMIT = /^[a-f0-9]{40,64}$/u;
18
+ const CUTOVER_STRATEGIES = new Set(["migrate", "retire", "disabled"]);
19
+
20
+ function cutoverError(code, message, details = {}) {
21
+ const error = new Error(message);
22
+ error.name = "GraphCutoverLedgerError";
23
+ error.code = code;
24
+ Object.assign(error, details);
25
+ return error;
26
+ }
27
+
28
+ function identifier(value, field) {
29
+ const text = String(value || "").trim();
30
+ if (!IDENTIFIER.test(text)) {
31
+ throw cutoverError(
32
+ "CC_GRAPH_CUTOVER_ARGUMENT_INVALID",
33
+ `${field} is not a valid cutover identifier`,
34
+ );
35
+ }
36
+ return text;
37
+ }
38
+
39
+ function digest(value, field) {
40
+ const text = String(value || "");
41
+ if (!DIGEST.test(text)) {
42
+ throw cutoverError(
43
+ "CC_GRAPH_CUTOVER_EVIDENCE_INVALID",
44
+ `${field} must be a sha256 digest`,
45
+ );
46
+ }
47
+ return text;
48
+ }
49
+
50
+ function storeInventory(value, { allowEmpty = false } = {}) {
51
+ if (!Array.isArray(value) || (!allowEmpty && value.length === 0)) {
52
+ throw cutoverError(
53
+ "CC_GRAPH_CUTOVER_STORE_INVENTORY_REQUIRED",
54
+ "cutover entries require a non-empty durable store inventory",
55
+ );
56
+ }
57
+ const normalized = value.map((entry) => identifier(entry, "store"));
58
+ if (new Set(normalized).size !== normalized.length) {
59
+ throw cutoverError(
60
+ "CC_GRAPH_CUTOVER_STORE_INVENTORY_INVALID",
61
+ "cutover store inventory contains duplicate stores",
62
+ );
63
+ }
64
+ return normalized.sort();
65
+ }
66
+
67
+ function cutoverStrategy(value) {
68
+ const strategy = String(value || "migrate").trim();
69
+ if (!CUTOVER_STRATEGIES.has(strategy)) {
70
+ throw cutoverError(
71
+ "CC_GRAPH_CUTOVER_STRATEGY_INVALID",
72
+ "cutoverStrategy must be migrate, retire, or disabled",
73
+ { cutoverStrategy: value },
74
+ );
75
+ }
76
+ return strategy;
77
+ }
78
+
79
+ function zero(value, field) {
80
+ if (Number(value) !== 0) {
81
+ throw cutoverError(
82
+ "CC_GRAPH_CUTOVER_GATE_FAILED",
83
+ `${field} must be zero before cutover`,
84
+ { field, value },
85
+ );
86
+ }
87
+ return 0;
88
+ }
89
+
90
+ function positive(value, field) {
91
+ const number = Number(value);
92
+ if (!Number.isSafeInteger(number) || number < 1) {
93
+ throw cutoverError(
94
+ "CC_GRAPH_CUTOVER_GATE_FAILED",
95
+ `${field} must be a positive safe integer`,
96
+ { field, value },
97
+ );
98
+ }
99
+ return number;
100
+ }
101
+
102
+ function percent(value, { allowZero = false } = {}) {
103
+ const number = Number(value);
104
+ if (
105
+ !Number.isFinite(number) ||
106
+ number > 100 ||
107
+ (allowZero ? number < 0 : number <= 0)
108
+ ) {
109
+ throw cutoverError(
110
+ "CC_GRAPH_CUTOVER_EVIDENCE_INVALID",
111
+ "canaryPercent must be within the allowed rollout range",
112
+ );
113
+ }
114
+ return number;
115
+ }
116
+
117
+ function platformEvidence(value) {
118
+ if (!Array.isArray(value)) {
119
+ throw cutoverError(
120
+ "CC_GRAPH_CUTOVER_PLATFORM_EVIDENCE_REQUIRED",
121
+ "canonical default requires same-SHA platform evidence",
122
+ );
123
+ }
124
+ const normalized = value.map((entry) => {
125
+ const realJourney =
126
+ entry?.schema === "chainlesschain.graph-agent-real-journey/v1" &&
127
+ entry?.status === "passed" &&
128
+ DIGEST.test(String(entry?.terminalEventDigest || "")) &&
129
+ DIGEST.test(String(entry?.evidenceDigest || ""));
130
+ return {
131
+ platform: String(entry?.platform || "").toLowerCase(),
132
+ commitSha: String(entry?.commitSha || "").toLowerCase(),
133
+ passed: entry?.passed === true || realJourney,
134
+ sourceEvidenceDigest: realJourney ? entry.evidenceDigest : null,
135
+ };
136
+ });
137
+ for (const platform of GRAPH_CUTOVER_REQUIRED_PLATFORMS) {
138
+ const matches = normalized.filter((entry) => entry.platform === platform);
139
+ if (
140
+ matches.length !== 1 ||
141
+ matches[0].passed !== true ||
142
+ !COMMIT.test(matches[0].commitSha)
143
+ ) {
144
+ throw cutoverError(
145
+ "CC_GRAPH_CUTOVER_PLATFORM_EVIDENCE_REQUIRED",
146
+ `canonical default requires one passing ${platform} result`,
147
+ );
148
+ }
149
+ }
150
+ if (
151
+ new Set(normalized.map((entry) => entry.commitSha)).size !== 1 ||
152
+ normalized.length !== GRAPH_CUTOVER_REQUIRED_PLATFORMS.length
153
+ ) {
154
+ throw cutoverError(
155
+ "CC_GRAPH_CUTOVER_SHA_MISMATCH",
156
+ "cutover platform evidence must bind one exact commit SHA",
157
+ );
158
+ }
159
+ return normalized.sort((left, right) =>
160
+ left.platform.localeCompare(right.platform),
161
+ );
162
+ }
163
+
164
+ function migrationCutpoints(value, stores) {
165
+ if (!Array.isArray(value)) {
166
+ throw cutoverError(
167
+ "CC_GRAPH_CUTOVER_MIGRATION_EVIDENCE_REQUIRED",
168
+ "canonical default requires one migration cutpoint per store",
169
+ );
170
+ }
171
+ const expected = storeInventory(stores);
172
+ const normalized = value.map((entry) => ({
173
+ store: identifier(entry?.store, "migrationCutpoints.store"),
174
+ cutpointDigest: digest(
175
+ entry?.cutpointDigest,
176
+ "migrationCutpoints.cutpointDigest",
177
+ ),
178
+ recoveryReceiptDigest: digest(
179
+ entry?.recoveryReceiptDigest,
180
+ "migrationCutpoints.recoveryReceiptDigest",
181
+ ),
182
+ rollbackDrillDigest: digest(
183
+ entry?.rollbackDrillDigest,
184
+ "migrationCutpoints.rollbackDrillDigest",
185
+ ),
186
+ rpoLossCount: zero(entry?.rpoLossCount, "migrationCutpoints.rpoLossCount"),
187
+ recovered: entry?.recovered === true,
188
+ }));
189
+ const actual = normalized.map((entry) => entry.store).sort();
190
+ if (
191
+ actual.length !== expected.length ||
192
+ actual.some((store, index) => store !== expected[index]) ||
193
+ normalized.some((entry) => !entry.recovered)
194
+ ) {
195
+ throw cutoverError(
196
+ "CC_GRAPH_CUTOVER_MIGRATION_EVIDENCE_INCOMPLETE",
197
+ "migration evidence must prove RPO=0 recovery for every declared store",
198
+ { expectedStores: expected, actualStores: actual },
199
+ );
200
+ }
201
+ return normalized.sort((left, right) =>
202
+ left.store.localeCompare(right.store),
203
+ );
204
+ }
205
+
206
+ function storeCoverageEvidence(value, state, commitSha) {
207
+ if (
208
+ !value ||
209
+ value.schema !== "chainlesschain.graph-store-cutover-coverage/v1" ||
210
+ !DIGEST.test(String(value.evidenceDigest || ""))
211
+ ) {
212
+ throw cutoverError(
213
+ "CC_GRAPH_CUTOVER_STORE_MATRIX_REQUIRED",
214
+ "canonical default requires the exact-SHA three-platform store matrix",
215
+ );
216
+ }
217
+ const unsigned = { ...value };
218
+ delete unsigned.evidenceDigest;
219
+ if (graphStoreEvidenceDigest(unsigned) !== value.evidenceDigest) {
220
+ throw cutoverError(
221
+ "CC_GRAPH_CUTOVER_STORE_MATRIX_INVALID",
222
+ "store cutover matrix digest does not match its contents",
223
+ );
224
+ }
225
+ if (String(value.commitSha || "").toLowerCase() !== commitSha) {
226
+ throw cutoverError(
227
+ "CC_GRAPH_CUTOVER_SHA_MISMATCH",
228
+ "store cutover matrix must bind the platform journey commit SHA",
229
+ );
230
+ }
231
+ const requiredPlatforms = (value.requiredPlatforms || [])
232
+ .map((platform) => String(platform).toLowerCase())
233
+ .sort();
234
+ if (
235
+ JSON.stringify(requiredPlatforms) !==
236
+ JSON.stringify([...GRAPH_CUTOVER_REQUIRED_PLATFORMS].sort())
237
+ ) {
238
+ throw cutoverError(
239
+ "CC_GRAPH_CUTOVER_STORE_MATRIX_INCOMPLETE",
240
+ "store cutover matrix does not require every supported platform",
241
+ );
242
+ }
243
+ const matches = (value.entries || []).filter(
244
+ (entry) =>
245
+ entry?.surface === state.surface && entry?.entryId === state.entryId,
246
+ );
247
+ const entry = matches.length === 1 ? matches[0] : null;
248
+ const stores = [...(entry?.stores || [])].sort();
249
+ const expectedStores = [...state.stores].sort();
250
+ const platformCoverage = entry?.platformCoverage || [];
251
+ if (
252
+ !entry ||
253
+ entry.complete !== true ||
254
+ JSON.stringify(stores) !== JSON.stringify(expectedStores) ||
255
+ platformCoverage.length !== expectedStores.length ||
256
+ platformCoverage.some(
257
+ (store) =>
258
+ store?.complete !== true ||
259
+ !expectedStores.includes(store.store) ||
260
+ JSON.stringify([...(store.coveredPlatforms || [])].sort()) !==
261
+ JSON.stringify(requiredPlatforms) ||
262
+ (store.missingPlatforms || []).length !== 0,
263
+ )
264
+ ) {
265
+ throw cutoverError(
266
+ "CC_GRAPH_CUTOVER_STORE_MATRIX_INCOMPLETE",
267
+ "store cutover matrix does not prove every entry store on all platforms",
268
+ { surface: state.surface, entryId: state.entryId },
269
+ );
270
+ }
271
+ return {
272
+ evidenceDigest: value.evidenceDigest,
273
+ commitSha,
274
+ requiredPlatforms,
275
+ storeCount: expectedStores.length,
276
+ };
277
+ }
278
+
279
+ function forwardEvidence(from, to, input, state) {
280
+ if (from === "legacy" && to === "shadow") {
281
+ const inventoryDigest = digest(input.inventoryDigest, "inventoryDigest");
282
+ if (inventoryDigest !== state.manifestDigest) {
283
+ throw cutoverError(
284
+ "CC_GRAPH_CUTOVER_MANIFEST_EVIDENCE_MISMATCH",
285
+ "shadow evidence does not match the manifest bound to this entry",
286
+ );
287
+ }
288
+ return {
289
+ inventoryDigest,
290
+ unknownWriterCount: zero(input.unknownWriterCount, "unknownWriterCount"),
291
+ shadowEffectInvocationCount: zero(
292
+ input.shadowEffectInvocationCount,
293
+ "shadowEffectInvocationCount",
294
+ ),
295
+ };
296
+ }
297
+ if (from === "shadow" && to === "canary") {
298
+ return {
299
+ shadowReportDigest: digest(
300
+ input.shadowReportDigest,
301
+ "shadowReportDigest",
302
+ ),
303
+ shadowRunCount: positive(input.shadowRunCount, "shadowRunCount"),
304
+ divergenceCount: zero(input.divergenceCount, "divergenceCount"),
305
+ unknownEffectCount: zero(input.unknownEffectCount, "unknownEffectCount"),
306
+ shadowEffectInvocationCount: zero(
307
+ input.shadowEffectInvocationCount,
308
+ "shadowEffectInvocationCount",
309
+ ),
310
+ canaryPercent: percent(input.canaryPercent),
311
+ optInOnly: input.optInOnly === true,
312
+ };
313
+ }
314
+ if (from === "canary" && to === "canonical") {
315
+ const platforms = platformEvidence(input.platformEvidence);
316
+ const canonicalEvidence = {
317
+ canaryReportDigest: digest(
318
+ input.canaryReportDigest,
319
+ "canaryReportDigest",
320
+ ),
321
+ canaryRunCount: positive(input.canaryRunCount, "canaryRunCount"),
322
+ canaryFailureCount: zero(input.canaryFailureCount, "canaryFailureCount"),
323
+ reconciliationCount: zero(
324
+ input.reconciliationCount,
325
+ "reconciliationCount",
326
+ ),
327
+ platformEvidence: platforms,
328
+ };
329
+ if (state.cutoverStrategy === "retire") {
330
+ return {
331
+ ...canonicalEvidence,
332
+ retirementProbeDigest: digest(
333
+ input.retirementProbeDigest,
334
+ "retirementProbeDigest",
335
+ ),
336
+ activeLegacyRunCount: zero(
337
+ input.activeLegacyRunCount,
338
+ "activeLegacyRunCount",
339
+ ),
340
+ legacyMutationSuccessCount: zero(
341
+ input.legacyMutationSuccessCount,
342
+ "legacyMutationSuccessCount",
343
+ ),
344
+ };
345
+ }
346
+ return {
347
+ ...canonicalEvidence,
348
+ migrationCutpoints: migrationCutpoints(
349
+ input.migrationCutpoints,
350
+ state.stores,
351
+ ),
352
+ storeCoverageEvidence: storeCoverageEvidence(
353
+ input.storeCoverageEvidence,
354
+ state,
355
+ platforms[0].commitSha,
356
+ ),
357
+ };
358
+ }
359
+ if (from === "canonical" && to === "legacy_read_only") {
360
+ return {
361
+ writerInventoryDigest: digest(
362
+ input.writerInventoryDigest,
363
+ "writerInventoryDigest",
364
+ ),
365
+ legacyWriterProbeDigest: digest(
366
+ input.legacyWriterProbeDigest,
367
+ "legacyWriterProbeDigest",
368
+ ),
369
+ legacyWriterProbeCount: zero(
370
+ input.legacyWriterProbeCount,
371
+ "legacyWriterProbeCount",
372
+ ),
373
+ };
374
+ }
375
+ if (from === "legacy_read_only" && to === "canonical") {
376
+ return {
377
+ incidentDigest: digest(input.incidentDigest, "incidentDigest"),
378
+ legacyWriterProbeCount: zero(
379
+ input.legacyWriterProbeCount,
380
+ "legacyWriterProbeCount",
381
+ ),
382
+ };
383
+ }
384
+ return null;
385
+ }
386
+
387
+ function rollbackEvidence(from, to, input) {
388
+ if (from === "shadow" && to === "legacy") {
389
+ return {
390
+ incidentDigest: digest(input.incidentDigest, "incidentDigest"),
391
+ shadowEffectInvocationCount: zero(
392
+ input.shadowEffectInvocationCount,
393
+ "shadowEffectInvocationCount",
394
+ ),
395
+ activeCanonicalWriterCount: zero(
396
+ input.activeCanonicalWriterCount,
397
+ "activeCanonicalWriterCount",
398
+ ),
399
+ };
400
+ }
401
+ if (
402
+ (from === "canary" && to === "shadow") ||
403
+ (from === "canonical" && to === "canary")
404
+ ) {
405
+ if (input.existingCanonicalRunsRetained !== true) {
406
+ throw cutoverError(
407
+ "CC_GRAPH_CUTOVER_ROLLBACK_UNSAFE",
408
+ "rollback must retain canonical authority for existing runs",
409
+ );
410
+ }
411
+ return {
412
+ incidentDigest: digest(input.incidentDigest, "incidentDigest"),
413
+ activeDispatchCount: zero(
414
+ input.activeDispatchCount,
415
+ "activeDispatchCount",
416
+ ),
417
+ existingCanonicalRunsRetained: true,
418
+ canaryPercent:
419
+ from === "canonical"
420
+ ? percent(input.canaryPercent ?? 0, { allowZero: true })
421
+ : 0,
422
+ };
423
+ }
424
+ return null;
425
+ }
426
+
427
+ function normalizeEvidence(from, to, evidence, state) {
428
+ const input = evidence && typeof evidence === "object" ? evidence : {};
429
+ const normalized =
430
+ forwardEvidence(from, to, input, state) ||
431
+ rollbackEvidence(from, to, input);
432
+ if (!normalized) {
433
+ throw cutoverError(
434
+ "CC_GRAPH_CUTOVER_TRANSITION_UNSUPPORTED",
435
+ `no durable evidence contract exists for ${from} -> ${to}`,
436
+ );
437
+ }
438
+ return normalized;
439
+ }
440
+
441
+ function clone(value) {
442
+ return value == null ? value : JSON.parse(JSON.stringify(value));
443
+ }
444
+
445
+ function stateFrom(events) {
446
+ return [...events]
447
+ .reverse()
448
+ .find(
449
+ (event) => event.payload?.state?.schema === GRAPH_CUTOVER_LEDGER_SCHEMA,
450
+ )?.payload?.state;
451
+ }
452
+
453
+ function projection(state, events) {
454
+ return Object.freeze({
455
+ ...clone(state),
456
+ eventSeq: events.at(-1)?.event_seq || 0,
457
+ eventHead: events.at(-1)?.hash || null,
458
+ });
459
+ }
460
+
461
+ export class GraphCutoverLedger {
462
+ constructor({ store = new JsonlRolloutStore(), now = Date.now } = {}) {
463
+ this.store = store;
464
+ this.now = now;
465
+ }
466
+
467
+ _thread(surface, entryId) {
468
+ return `graph-cutover:${identifier(surface, "surface")}:${identifier(
469
+ entryId,
470
+ "entryId",
471
+ )}`;
472
+ }
473
+
474
+ begin({
475
+ surface,
476
+ entryId,
477
+ manifestDigest,
478
+ stores,
479
+ cutoverStrategy: strategyInput = "migrate",
480
+ }) {
481
+ const safeSurface = identifier(surface, "surface");
482
+ const safeEntryId = identifier(entryId, "entryId");
483
+ const safeManifestDigest = digest(manifestDigest, "manifestDigest");
484
+ const safeCutoverStrategy = cutoverStrategy(strategyInput);
485
+ const safeStores = storeInventory(stores, {
486
+ allowEmpty: safeCutoverStrategy !== "migrate",
487
+ });
488
+ const threadId = this._thread(safeSurface, safeEntryId);
489
+ this.store.start({
490
+ threadId,
491
+ title: `Graph cutover ${safeSurface}/${safeEntryId}`,
492
+ metadata: {
493
+ kind: "graph_cutover",
494
+ surface: safeSurface,
495
+ entryId: safeEntryId,
496
+ },
497
+ });
498
+ let events = this.store.read(threadId);
499
+ const existing = stateFrom(events);
500
+ if (existing) {
501
+ if (existing.manifestDigest !== safeManifestDigest) {
502
+ throw cutoverError(
503
+ "CC_GRAPH_CUTOVER_MANIFEST_CONFLICT",
504
+ "cutover entry is already bound to a different manifest",
505
+ );
506
+ }
507
+ if (JSON.stringify(existing.stores) !== JSON.stringify(safeStores)) {
508
+ throw cutoverError(
509
+ "CC_GRAPH_CUTOVER_STORE_INVENTORY_CONFLICT",
510
+ "cutover entry is already bound to a different store inventory",
511
+ );
512
+ }
513
+ if ((existing.cutoverStrategy || "migrate") !== safeCutoverStrategy) {
514
+ throw cutoverError(
515
+ "CC_GRAPH_CUTOVER_STRATEGY_CONFLICT",
516
+ "cutover entry is already bound to a different strategy",
517
+ );
518
+ }
519
+ return projection(existing, events);
520
+ }
521
+ const timestamp = new Date(this.now()).toISOString();
522
+ const state = {
523
+ schema: GRAPH_CUTOVER_LEDGER_SCHEMA,
524
+ surface: safeSurface,
525
+ entryId: safeEntryId,
526
+ manifestDigest: safeManifestDigest,
527
+ cutoverStrategy: safeCutoverStrategy,
528
+ stores: safeStores,
529
+ stage: "legacy",
530
+ canaryPercent: 0,
531
+ optInOnly: false,
532
+ transitionCount: 0,
533
+ rollbackCount: 0,
534
+ lastEvidenceDigest: null,
535
+ createdAt: timestamp,
536
+ updatedAt: timestamp,
537
+ };
538
+ this.store.append({
539
+ threadId,
540
+ eventType: "cutover.initialized",
541
+ idempotencyKey: `cutover:${safeSurface}:${safeEntryId}:initialized`,
542
+ payload: { state },
543
+ expectedRevision: events.at(-1)?.event_seq,
544
+ expectedHeadHash: events.at(-1)?.hash,
545
+ });
546
+ events = this.store.read(threadId);
547
+ return projection(stateFrom(events), events);
548
+ }
549
+
550
+ recover(surface, entryId) {
551
+ const events = this.store.read(this._thread(surface, entryId));
552
+ const state = stateFrom(events);
553
+ if (!state) {
554
+ throw cutoverError(
555
+ "CC_GRAPH_CUTOVER_NOT_FOUND",
556
+ `Graph cutover was not found: ${surface}/${entryId}`,
557
+ );
558
+ }
559
+ return projection(state, events);
560
+ }
561
+
562
+ transition(surface, entryId, to, evidence = {}, { expectedEventHead } = {}) {
563
+ const current = this.recover(surface, entryId);
564
+ if (
565
+ expectedEventHead !== undefined &&
566
+ current.eventHead !== expectedEventHead
567
+ ) {
568
+ throw cutoverError(
569
+ "CC_GRAPH_CUTOVER_HEAD_CONFLICT",
570
+ "cutover ledger changed before transition",
571
+ { expectedEventHead, actualEventHead: current.eventHead },
572
+ );
573
+ }
574
+ if (current.stage === to) return current;
575
+ if ((current.cutoverStrategy || "migrate") === "disabled") {
576
+ throw cutoverError(
577
+ "CC_GRAPH_CUTOVER_ENTRY_DISABLED",
578
+ "disabled runtime entries cannot advance through rollout stages",
579
+ { surface, entryId, stage: current.stage, requestedStage: to },
580
+ );
581
+ }
582
+ assertGraphCutoverTransition(current.stage, to);
583
+ const normalized = normalizeEvidence(current.stage, to, evidence, current);
584
+ const rollback = [
585
+ "shadow:legacy",
586
+ "canary:shadow",
587
+ "canonical:canary",
588
+ ].includes(`${current.stage}:${to}`);
589
+ const next = {
590
+ ...clone(current),
591
+ stage: to,
592
+ canaryPercent:
593
+ to === "canary"
594
+ ? normalized.canaryPercent
595
+ : ["canonical", "legacy_read_only"].includes(to)
596
+ ? 100
597
+ : 0,
598
+ optInOnly: to === "canary" ? normalized.optInOnly === true : false,
599
+ transitionCount: current.transitionCount + 1,
600
+ rollbackCount: current.rollbackCount + (rollback ? 1 : 0),
601
+ lastEvidenceDigest: graphDigest(
602
+ { from: current.stage, to, evidence: normalized },
603
+ "cc.graph.cutover-evidence/v1",
604
+ ),
605
+ updatedAt: new Date(this.now()).toISOString(),
606
+ };
607
+ delete next.eventSeq;
608
+ delete next.eventHead;
609
+ const threadId = this._thread(surface, entryId);
610
+ const events = this.store.read(threadId);
611
+ this.store.append({
612
+ threadId,
613
+ eventType: `cutover.${to}`,
614
+ idempotencyKey: `cutover:${current.transitionCount + 1}:${current.stage}:${to}`,
615
+ payload: {
616
+ from: current.stage,
617
+ to,
618
+ evidence: normalized,
619
+ state: next,
620
+ },
621
+ expectedRevision: events.at(-1)?.event_seq,
622
+ expectedHeadHash: current.eventHead,
623
+ });
624
+ const updatedEvents = this.store.read(threadId);
625
+ return projection(stateFrom(updatedEvents), updatedEvents);
626
+ }
627
+
628
+ authorityMode(surface, entryId, { runKey, optIn = false } = {}) {
629
+ const state = this.recover(surface, entryId);
630
+ if ((state.cutoverStrategy || "migrate") === "disabled") return "legacy";
631
+ if (state.stage === "legacy") return "legacy";
632
+ if (state.stage === "shadow") return "shadow";
633
+ if (["canonical", "legacy_read_only"].includes(state.stage)) {
634
+ return "canonical";
635
+ }
636
+ if (state.optInOnly) return optIn === true ? "canonical" : "shadow";
637
+ const key = identifier(runKey, "runKey");
638
+ const bucket =
639
+ Number.parseInt(
640
+ createHash("sha256")
641
+ .update(`${state.surface}:${state.entryId}:${key}`, "utf8")
642
+ .digest("hex")
643
+ .slice(0, 8),
644
+ 16,
645
+ ) % 10_000;
646
+ return bucket < Math.round(state.canaryPercent * 100)
647
+ ? "canonical"
648
+ : "shadow";
649
+ }
650
+ }
@@ -32,15 +32,29 @@ export class GraphEventStore {
32
32
  return this.head(runId);
33
33
  }
34
34
 
35
- append(runId, type, payload, { idempotencyKey = null, traceId = null } = {}) {
35
+ append(
36
+ runId,
37
+ type,
38
+ payload,
39
+ {
40
+ idempotencyKey = null,
41
+ traceId = null,
42
+ authority = null,
43
+ expectedRevision = undefined,
44
+ expectedHeadHash = undefined,
45
+ } = {},
46
+ ) {
36
47
  const record = this.rolloutStore.append({
37
48
  threadId: runId,
38
49
  eventType: `graph.${type}`,
39
50
  traceId,
40
51
  idempotencyKey,
52
+ expectedRevision,
53
+ expectedHeadHash,
41
54
  payload: {
42
55
  schema: GRAPH_EVENT_SCHEMA,
43
56
  type,
57
+ authority: clone(authority),
44
58
  ...clone(payload),
45
59
  },
46
60
  });