chainlesschain 0.166.6 → 0.166.9

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 (214) hide show
  1. package/README.md +21 -0
  2. package/package.json +5 -1
  3. package/src/assets/web-panel/.build-hash +1 -1
  4. package/src/assets/web-panel/assets/{AIOps-CVnFjTfG.js → AIOps-DJvpifGx.js} +1 -1
  5. package/src/assets/web-panel/assets/{ActionButton-BxkwuuQC.js → ActionButton-D2UhuF_A.js} +1 -1
  6. package/src/assets/web-panel/assets/{Analytics-C2670q6z.js → Analytics-C9ho9obu.js} +3 -3
  7. package/src/assets/web-panel/assets/{AppLayout-CU_rylW6.js → AppLayout-BvRCpFK3.js} +3 -3
  8. package/src/assets/web-panel/assets/{Artifacts-DGJmYqnj.js → Artifacts-jxrySvpk.js} +1 -1
  9. package/src/assets/web-panel/assets/{Audit-BZOQbINJ.js → Audit-ovEw0ZYl.js} +1 -1
  10. package/src/assets/web-panel/assets/{BackgroundAgents-DghB4QwF.js → BackgroundAgents-D7H5z6zS.js} +1 -1
  11. package/src/assets/web-panel/assets/{Backup-l0mwPbta.js → Backup-BuXob8za.js} +1 -1
  12. package/src/assets/web-panel/assets/{BaseInput-Bv3WSZOh.js → BaseInput-CtnszuQ2.js} +1 -1
  13. package/src/assets/web-panel/assets/{Chat-s88nBdvt.js → Chat-B_mYE8fJ.js} +6 -6
  14. package/src/assets/web-panel/assets/{ChatBubbleRenderer-Z8CBuB1a.js → ChatBubbleRenderer-DaiA6CsX.js} +1 -1
  15. package/src/assets/web-panel/assets/{Checkbox-B5icasEs.js → Checkbox-ierdtcJD.js} +1 -1
  16. package/src/assets/web-panel/assets/{Codegen-CsUFqNud.js → Codegen-3VKPJD_B.js} +1 -1
  17. package/src/assets/web-panel/assets/{Col-DlBtClaW.js → Col-1EdMyf5P.js} +1 -1
  18. package/src/assets/web-panel/assets/{Community-xRn1IdLB.js → Community-D6H7UA8K.js} +1 -1
  19. package/src/assets/web-panel/assets/{Compact-naIr8a_o.js → Compact-BBAZvYIm.js} +1 -1
  20. package/src/assets/web-panel/assets/{Compliance-Dp9esfFE.js → Compliance-CgsTrAoj.js} +1 -1
  21. package/src/assets/web-panel/assets/{Cowork-DQlMYJB-.js → Cowork-tnZgLoTE.js} +4 -4
  22. package/src/assets/web-panel/assets/{Cron-Cd0oNLzJ.js → Cron-CPpZw2Nh.js} +2 -2
  23. package/src/assets/web-panel/assets/{Crosschain-BtDIx3XX.js → Crosschain-BymBcp5z.js} +1 -1
  24. package/src/assets/web-panel/assets/{DID-BOlpzEwg.js → DID-DKPxS_K1.js} +2 -2
  25. package/src/assets/web-panel/assets/{Dashboard-DV15Axk1.js → Dashboard-DvJhEmH9.js} +2 -2
  26. package/src/assets/web-panel/assets/{Dropdown-COXrewN1.js → Dropdown-b8iQyQIZ.js} +1 -1
  27. package/src/assets/web-panel/assets/{EmailListRenderer-EjIt0Cm7.js → EmailListRenderer--0-A-zKb.js} +1 -1
  28. package/src/assets/web-panel/assets/{FamilyGuardDashboard-DJlhwb-R.js → FamilyGuardDashboard-C12iDN06.js} +1 -1
  29. package/src/assets/web-panel/assets/{Federation-BMiFl82k.js → Federation-BRhaE2Z8.js} +1 -1
  30. package/src/assets/web-panel/assets/{FormItemContext-BUcDQyxn.js → FormItemContext-C4dnq7yf.js} +1 -1
  31. package/src/assets/web-panel/assets/{GenericCardRenderer-C25uFxvK.js → GenericCardRenderer-CGAdbR5n.js} +1 -1
  32. package/src/assets/web-panel/assets/{Git-D20uaEUU.js → Git-4eA2fYzL.js} +2 -2
  33. package/src/assets/web-panel/assets/{Governance-CQxYJjCX.js → Governance-BJuY9Y-9.js} +1 -1
  34. package/src/assets/web-panel/assets/{Inference-m-sczpS5.js → Inference-DZ5Dy4up.js} +1 -1
  35. package/src/assets/web-panel/assets/{KnowledgeGraph-CBsIlU0S.js → KnowledgeGraph-BMTjRs4A.js} +1 -1
  36. package/src/assets/web-panel/assets/{Logs-D0-5bqeu.js → Logs-BKEeQEfJ.js} +2 -2
  37. package/src/assets/web-panel/assets/MarkdownRenderer-B05DgASC.js +1 -0
  38. package/src/assets/web-panel/assets/{Marketplace-CwDGFu2Q.js → Marketplace-Dhho4fEg.js} +1 -1
  39. package/src/assets/web-panel/assets/{McpTools-7FxJ3omT.js → McpTools-BW6xxSlU.js} +2 -2
  40. package/src/assets/web-panel/assets/{Memory-DOVyb59U.js → Memory-mPqlpSkq.js} +2 -2
  41. package/src/assets/web-panel/assets/{MobileBridge-DZXl-Hje.js → MobileBridge-Cb9N_DQI.js} +1 -1
  42. package/src/assets/web-panel/assets/{MobileProjects-DlZ8tRXt.js → MobileProjects-BNs8tah_.js} +1 -1
  43. package/src/assets/web-panel/assets/{Mtc-D7Pjx1s_.js → Mtc-CaxBt8tn.js} +5 -5
  44. package/src/assets/web-panel/assets/{MtcAudit-BND7lX8p.js → MtcAudit-DRPQbp3t.js} +2 -2
  45. package/src/assets/web-panel/assets/{Multisig-BknS7B-p.js → Multisig-CRm-d8bK.js} +3 -3
  46. package/src/assets/web-panel/assets/{NLProgramming-7C2-KPHt.js → NLProgramming-BCCk3LPs.js} +1 -1
  47. package/src/assets/web-panel/assets/{Notes-DNWdTndN.js → Notes-Cv8SN8gN.js} +4 -4
  48. package/src/assets/web-panel/assets/{NotificationSettings-DCm4KcJZ.js → NotificationSettings-CggIvTh5.js} +1 -1
  49. package/src/assets/web-panel/assets/{OrderTableRenderer-DyVwwjJx.js → OrderTableRenderer-BW3_bV12.js} +1 -1
  50. package/src/assets/web-panel/assets/{Organization-Dl2O_qJ9.js → Organization-BuRE-VCO.js} +4 -4
  51. package/src/assets/web-panel/assets/{Overflow-BOFCvk9Q.js → Overflow-DBrm1gju.js} +1 -1
  52. package/src/assets/web-panel/assets/{P2P-DgdVowY1.js → P2P-CWVnDYgS.js} +2 -2
  53. package/src/assets/web-panel/assets/PdhVaultBrowser-8RKUIpte.js +7 -0
  54. package/src/assets/web-panel/assets/{Permissions-BHNEUUwa.js → Permissions-tDUAxogW.js} +4 -4
  55. package/src/assets/web-panel/assets/{PersonalDataHub-CaZbrOdz.js → PersonalDataHub-Dv2-CFis.js} +4 -4
  56. package/src/assets/web-panel/assets/{Pipeline-DJDnoXmE.js → Pipeline-DM6kvFni.js} +1 -1
  57. package/src/assets/web-panel/assets/{Privacy-CBQr2eX8.js → Privacy-DzK-yL0d.js} +1 -1
  58. package/src/assets/web-panel/assets/{ProjectInit-Yghqa06z.js → ProjectInit-B5UrNpkj.js} +2 -2
  59. package/src/assets/web-panel/assets/{ProjectSettings-HBAtJi8U.js → ProjectSettings-CxJD43oW.js} +2 -2
  60. package/src/assets/web-panel/assets/Projects-Z2Orm8Ma.js +1 -0
  61. package/src/assets/web-panel/assets/{Providers-uJTz77GI.js → Providers-CYl4jRYK.js} +1 -1
  62. package/src/assets/web-panel/assets/{QrScannerModal-k3M5ouTp.js → QrScannerModal-DeH2T0fH.js} +1 -1
  63. package/src/assets/web-panel/assets/{QuickAsk-CMji45PH.js → QuickAsk-Bw9k_kTk.js} +1 -1
  64. package/src/assets/web-panel/assets/{Recommend-DyoZYPls.js → Recommend-Bl_jGwoi.js} +1 -1
  65. package/src/assets/web-panel/assets/RemoteSession-d6u01Kq0.js +5 -0
  66. package/src/assets/web-panel/assets/{Reputation-D4hIs4vV.js → Reputation-Ds_Dfr-k.js} +1 -1
  67. package/src/assets/web-panel/assets/{Row-mzE-ElTg.js → Row-BpQUQTNj.js} +1 -1
  68. package/src/assets/web-panel/assets/{RssFeed-cg7J3hI6.js → RssFeed-BatpZBGt.js} +3 -3
  69. package/src/assets/web-panel/assets/{Search-6M0TkGSZ.js → Search-D7hOObzi.js} +1 -1
  70. package/src/assets/web-panel/assets/{Security-B1GQ67qh.js → Security-dXQ4FKNt.js} +4 -4
  71. package/src/assets/web-panel/assets/{Services-D_RwU_5S.js → Services-D8wInGPa.js} +2 -2
  72. package/src/assets/web-panel/assets/{Skeleton-TZjk-dsW.js → Skeleton-DXQCoYpM.js} +1 -1
  73. package/src/assets/web-panel/assets/{Skills-CG6FcaCV.js → Skills-D3oLRIVn.js} +1 -1
  74. package/src/assets/web-panel/assets/{Sla-BI190QDv.js → Sla-BmP4-LnJ.js} +1 -1
  75. package/src/assets/web-panel/assets/{SpeechSettings-BWmxF2Wu.js → SpeechSettings-DLj-xzro.js} +1 -1
  76. package/src/assets/web-panel/assets/{SyncSettings-CwPiGw6L.js → SyncSettings-8KKaaRMr.js} +2 -2
  77. package/src/assets/web-panel/assets/{Tasks-BIXgCeSk.js → Tasks-GyK42I7C.js} +1 -1
  78. package/src/assets/web-panel/assets/{Templates-CQFgGCoK.js → Templates-n1y_8UBG.js} +1 -1
  79. package/src/assets/web-panel/assets/{Tenant-DAelEdkL.js → Tenant-B39nBph8.js} +1 -1
  80. package/src/assets/web-panel/assets/{Terminal-BbqWK6qh.js → Terminal-DCQoOKXe.js} +2 -2
  81. package/src/assets/web-panel/assets/{TimelineRenderer-cmG3zvY5.js → TimelineRenderer-BkhbDfXk.js} +1 -1
  82. package/src/assets/web-panel/assets/{Tokens-C8444NKU.js → Tokens-BeHIIU8D.js} +1 -1
  83. package/src/assets/web-panel/assets/{Trigger-C1Rpnz0I.js → Trigger-BHvWXVOb.js} +1 -1
  84. package/src/assets/web-panel/assets/{Trust-u4koPiFD.js → Trust-y2WaOSto.js} +1 -1
  85. package/src/assets/web-panel/assets/{UkeySign-oPWKn80Z.js → UkeySign-DBFeJBzt.js} +1 -1
  86. package/src/assets/web-panel/assets/{VideoEditing-D0CUUY_5.js → VideoEditing-BEOWitC-.js} +1 -1
  87. package/src/assets/web-panel/assets/{Wallet-fu9pIAbt.js → Wallet-BITedTe6.js} +4 -4
  88. package/src/assets/web-panel/assets/{WebAuthn-Bc5ciuOx.js → WebAuthn-DMwlJVin.js} +2 -2
  89. package/src/assets/web-panel/assets/{WorkflowEditor-BCin4_4N.js → WorkflowEditor-CKfGFEu1.js} +1 -1
  90. package/src/assets/web-panel/assets/{chat-B2jbM-XN.js → chat-DR6gnBGF.js} +1 -1
  91. package/src/assets/web-panel/assets/{colors-CUVoUQ_2.js → colors-C_Hw5NCn.js} +1 -1
  92. package/src/assets/web-panel/assets/{compact-item-DgOGpkRH.js → compact-item-B8IPwsEu.js} +1 -1
  93. package/src/assets/web-panel/assets/{createContext-DIxU6eKI.js → createContext-D-u4JVnJ.js} +1 -1
  94. package/src/assets/web-panel/assets/devWarning-Cq6lSd_q.js +1 -0
  95. package/src/assets/web-panel/assets/{hasIn-DoKRKMav.js → hasIn-CZubdS4D.js} +1 -1
  96. package/src/assets/web-panel/assets/{index-ByjrulIZ.js → index-11ASqx-A.js} +1 -1
  97. package/src/assets/web-panel/assets/index-BQ3213fz.js +1 -0
  98. package/src/assets/web-panel/assets/{index-BwYofQJg.js → index-BXS6wYJZ.js} +1 -1
  99. package/src/assets/web-panel/assets/{index-CLdpHe6w.js → index-Bi21Etg4.js} +1 -1
  100. package/src/assets/web-panel/assets/{index-2flsPFIX.js → index-Bizm87cX.js} +1 -1
  101. package/src/assets/web-panel/assets/{index-BXb-Smmm.js → index-Bv2132PO.js} +1 -1
  102. package/src/assets/web-panel/assets/{index-B5ySiGYy.js → index-BzoFanEQ.js} +1 -1
  103. package/src/assets/web-panel/assets/{index-CWbL1-N0.js → index-C25rOz7O.js} +1 -1
  104. package/src/assets/web-panel/assets/{index-Cj-us-D_.js → index-C2VmtNaQ.js} +1 -1
  105. package/src/assets/web-panel/assets/{index-C9TsO9T9.js → index-Cbm8F0M2.js} +1 -1
  106. package/src/assets/web-panel/assets/{index-eTWPaeBI.js → index-CfniPP5p.js} +1 -1
  107. package/src/assets/web-panel/assets/{index-D2yo6ast.js → index-Cg2bCeol.js} +1 -1
  108. package/src/assets/web-panel/assets/{index-CPXdBnGi.js → index-CiXkhOkY.js} +1 -1
  109. package/src/assets/web-panel/assets/{index-BCLeNqIL.js → index-Cl5RpreD.js} +1 -1
  110. package/src/assets/web-panel/assets/{index-BtSPhEyC.js → index-CqmLw8cs.js} +1 -1
  111. package/src/assets/web-panel/assets/{index-eYxUASSG.js → index-Ctje8Vl3.js} +1 -1
  112. package/src/assets/web-panel/assets/{index-i5MB455u.js → index-D1w96gg9.js} +1 -1
  113. package/src/assets/web-panel/assets/{index-CpvyOt5Z.js → index-D47jt8ET.js} +1 -1
  114. package/src/assets/web-panel/assets/{index-CeWSw-z5.js → index-DJ6pG79m.js} +1 -1
  115. package/src/assets/web-panel/assets/{index-B3F_p8NA.js → index-DTosx2Wu.js} +1 -1
  116. package/src/assets/web-panel/assets/{index-B9mSYovv.js → index-DY6MHo9D.js} +1 -1
  117. package/src/assets/web-panel/assets/{index-DJMHnbmu.js → index-DcFjRT-K.js} +1 -1
  118. package/src/assets/web-panel/assets/{index-D-ufbAJB.js → index-DfnzrUxU.js} +1 -1
  119. package/src/assets/web-panel/assets/{index-BFUprhNd.js → index-DjRrqooR.js} +1 -1
  120. package/src/assets/web-panel/assets/{index-Co4lDEk-.js → index-DmRcgiYT.js} +1 -1
  121. package/src/assets/web-panel/assets/{index-DGTZdZOE.js → index-DpjHpRoq.js} +3 -3
  122. package/src/assets/web-panel/assets/{index-DHugFl3v.js → index-Dt-w3xQ7.js} +1 -1
  123. package/src/assets/web-panel/assets/{index-B-f6l4Vm.js → index-DuOU5apg.js} +1 -1
  124. package/src/assets/web-panel/assets/{index-DtyAuGOm.js → index-LvjAKDSl.js} +1 -1
  125. package/src/assets/web-panel/assets/{index-be8SonCh.js → index-_nL79NT8.js} +1 -1
  126. package/src/assets/web-panel/assets/{index-BRMEO2Tl.js → index-auyNYjLo.js} +1 -1
  127. package/src/assets/web-panel/assets/{index-C88m9lsi.js → index-cVqbas5C.js} +1 -1
  128. package/src/assets/web-panel/assets/{index-CqlwIjJx.js → index-ffe7bvqP.js} +1 -1
  129. package/src/assets/web-panel/assets/{index-DpTFx8Xx.js → index-idZKyADQ.js} +1 -1
  130. package/src/assets/web-panel/assets/{index-Cskz--fd.js → index-k1Xa5iw1.js} +1 -1
  131. package/src/assets/web-panel/assets/{index-CF80xxPG.js → index-mYZJxGH6.js} +1 -1
  132. package/src/assets/web-panel/assets/{index-Ds8r3Fzb.js → index-rYBHSDcJ.js} +1 -1
  133. package/src/assets/web-panel/assets/index-vJagfET2.js +1 -0
  134. package/src/assets/web-panel/assets/{index-C846lPGD.js → index-xoNssgz_.js} +1 -1
  135. package/src/assets/web-panel/assets/{initDefaultProps-B4chl7iR.js → initDefaultProps-lcU_dT2r.js} +1 -1
  136. package/src/assets/web-panel/assets/{motion-DruN9h96.js → motion-Dc7UNXZ1.js} +1 -1
  137. package/src/assets/web-panel/assets/{move-Cl0UtCgU.js → move-Cll8ZXaL.js} +1 -1
  138. package/src/assets/web-panel/assets/{mtc-parser-FX2ttw70.js → mtc-parser-CAb_yLIM.js} +1 -1
  139. package/src/assets/web-panel/assets/{omit-BDCneY46.js → omit-DurYMyTe.js} +1 -1
  140. package/src/assets/web-panel/assets/{pickAttrs-DycsEg8b.js → pickAttrs-BITQyOlw.js} +1 -1
  141. package/src/assets/web-panel/assets/{placementArrow-Bc-dBuMv.js → placementArrow-Dg1rH91i.js} +1 -1
  142. package/src/assets/web-panel/assets/{responsiveObserve--Wwaf8lu.js → responsiveObserve-D_EWIeac.js} +1 -1
  143. package/src/assets/web-panel/assets/{slide-vDmHQqHH.js → slide-lK4d3zsh.js} +1 -1
  144. package/src/assets/web-panel/assets/{statusUtils-9c52yv3G.js → statusUtils-Bflq-Git.js} +1 -1
  145. package/src/assets/web-panel/assets/{styleChecker-BT4BiO6_.js → styleChecker-Ccp0I-wt.js} +1 -1
  146. package/src/assets/web-panel/assets/{useFlexGapSupport-C7XxWPcP.js → useFlexGapSupport-CYZbByYA.js} +1 -1
  147. package/src/assets/web-panel/assets/{useFs-BHoMqh4i.js → useFs-B_x0nxFD.js} +1 -1
  148. package/src/assets/web-panel/assets/{usePersonalDataHub-BOLxvsAG.js → usePersonalDataHub-DW55rL85.js} +1 -1
  149. package/src/assets/web-panel/assets/{vnode-CA7poeeP.js → vnode-Ca6MH3W8.js} +1 -1
  150. package/src/assets/web-panel/assets/{zoom-B_sAn-co.js → zoom-NdHelOog.js} +1 -1
  151. package/src/assets/web-panel/index.html +1 -1
  152. package/src/command-help-index.json +1 -1
  153. package/src/commands/graph.js +215 -5
  154. package/src/commands/orchestrate.js +61 -9
  155. package/src/commands/team-distributed.js +342 -2
  156. package/src/commands/team.js +169 -7
  157. package/src/data/changelog.json +46 -0
  158. package/src/gateways/ws/message-dispatcher.js +4 -0
  159. package/src/gateways/ws/session-protocol.js +89 -11
  160. package/src/gateways/ws/ws-agent-handler.js +65 -9
  161. package/src/gateways/ws/ws-approval-gate.js +181 -16
  162. package/src/gateways/ws/ws-server.js +16 -6
  163. package/src/generated/cc-agent-protocol.schema.json +154 -3
  164. package/src/lib/agent-team/task-lease.js +487 -3
  165. package/src/lib/agent-team/team-agent-stream.js +16 -0
  166. package/src/lib/agent-team/team-distributed-graph-bridge.js +257 -0
  167. package/src/lib/agent-team/team-distributed-graph-writer.js +523 -0
  168. package/src/lib/agent-team/team-distributed-queue.js +251 -0
  169. package/src/lib/agent-team/team-graph-runtime-adapter.js +1918 -0
  170. package/src/lib/agent-team/team-runner.js +285 -3
  171. package/src/lib/app-server/graph-runtime.js +1078 -0
  172. package/src/lib/app-server/rollout-store.js +32 -0
  173. package/src/lib/app-server/server.js +200 -0
  174. package/src/lib/approval-grant-ledger.js +69 -0
  175. package/src/lib/autonomous-agent.js +11 -0
  176. package/src/lib/codex-app-server-adapter.js +11 -20
  177. package/src/lib/cowork-graph-authority-adapter.js +455 -0
  178. package/src/lib/dynamic-workflow-facade.js +83 -16
  179. package/src/lib/graph-kernel/adapters.js +93 -20
  180. package/src/lib/graph-kernel/authority.js +324 -0
  181. package/src/lib/graph-kernel/compiler.js +67 -21
  182. package/src/lib/graph-kernel/cutover-authority-resolver.js +245 -0
  183. package/src/lib/graph-kernel/cutover-ledger.js +692 -0
  184. package/src/lib/graph-kernel/event-store.js +15 -1
  185. package/src/lib/graph-kernel/graph-runtime-surfaces.json +1427 -0
  186. package/src/lib/graph-kernel/index.js +4 -0
  187. package/src/lib/graph-kernel/migration-saga.js +309 -0
  188. package/src/lib/graph-kernel/retirement-evidence.js +567 -0
  189. package/src/lib/graph-kernel/runtime-surface-manifest.js +481 -0
  190. package/src/lib/graph-kernel/runtime.js +737 -38
  191. package/src/lib/graph-kernel/store-cutover-evidence.js +354 -0
  192. package/src/lib/graph-kernel/trace-reducer.js +6 -0
  193. package/src/lib/graph-kernel/trigger-adapter.js +60 -3
  194. package/src/lib/interaction-adapter.js +61 -9
  195. package/src/lib/legacy-runtime-authority.js +160 -0
  196. package/src/lib/orchestrator.js +28 -0
  197. package/src/lib/policy-decision-event.js +149 -0
  198. package/src/lib/process-execution-broker/platform-sandbox.js +15 -1
  199. package/src/lib/record-replay/index.js +1 -0
  200. package/src/lib/record-replay/playwright-ui-driver.js +327 -0
  201. package/src/lib/scheduler-kernel/graph-authority-adapter.js +512 -0
  202. package/src/lib/scheduler-kernel/runtime.js +75 -2
  203. package/src/lib/webhook-security.js +484 -36
  204. package/src/runtime/coding-agent-events.cjs +13 -11
  205. package/src/runtime/headless-runner.js +17 -1
  206. package/src/runtime/headless-stream.js +66 -3
  207. package/src/runtime/session-slash-commands.js +57 -6
  208. package/src/assets/web-panel/assets/MarkdownRenderer-BwH414Ms.js +0 -1
  209. package/src/assets/web-panel/assets/PdhVaultBrowser-BiaZfQIM.js +0 -7
  210. package/src/assets/web-panel/assets/Projects-DiCGnMjz.js +0 -1
  211. package/src/assets/web-panel/assets/RemoteSession-s1gR38IO.js +0 -5
  212. package/src/assets/web-panel/assets/devWarning-DiSlgCC4.js +0 -1
  213. package/src/assets/web-panel/assets/index-9WIQEHvv.js +0 -1
  214. package/src/assets/web-panel/assets/index-B7mDobAl.js +0 -1
@@ -0,0 +1,1078 @@
1
+ import { randomUUID } from "node:crypto";
2
+ import {
3
+ compileGraphDefinition,
4
+ graphDigest,
5
+ } from "../graph-kernel/compiler.js";
6
+ import { createGraphAuthorityBinding } from "../graph-kernel/authority.js";
7
+ import { GraphEventStore } from "../graph-kernel/event-store.js";
8
+ import { GraphKernel } from "../graph-kernel/runtime.js";
9
+ import {
10
+ diffGraphTrace,
11
+ locateBlockedRoot,
12
+ reduceGraphTrace,
13
+ } from "../graph-kernel/trace-reducer.js";
14
+
15
+ const TERMINAL = new Set([
16
+ "succeeded",
17
+ "failed",
18
+ "partial",
19
+ "cancelled",
20
+ "blocked",
21
+ "deadlocked",
22
+ "budget_exhausted",
23
+ "compensated",
24
+ "compensation_failed",
25
+ ]);
26
+ const DIGEST = /^sha256:[a-f0-9]{64}$/u;
27
+ const DEFAULT_HISTORY_EVENT_LIMIT = 1_000;
28
+ const MAX_HISTORY_EVENT_LIMIT = 2_000;
29
+ const DEFAULT_HISTORY_SNAPSHOT_LIMIT = 200;
30
+ const MAX_HISTORY_SNAPSHOT_LIMIT = 200;
31
+
32
+ function runtimeError(code, message, details = {}) {
33
+ const error = new Error(message);
34
+ error.name = "AppServerGraphRuntimeError";
35
+ error.code = code;
36
+ Object.assign(error, details);
37
+ return error;
38
+ }
39
+
40
+ function evidence(value) {
41
+ const source = value?.terminalEvidence || {};
42
+ const terminal = {};
43
+ if (DIGEST.test(String(source.outputDigest || ""))) {
44
+ terminal.outputDigest = source.outputDigest;
45
+ }
46
+ if (source.commit) terminal.commit = String(source.commit);
47
+ if (Array.isArray(source.artifactIds) && source.artifactIds.length) {
48
+ terminal.artifactIds = source.artifactIds.map(String);
49
+ }
50
+ if (Array.isArray(source.testReceiptIds) && source.testReceiptIds.length) {
51
+ terminal.testReceiptIds = source.testReceiptIds.map(String);
52
+ }
53
+ if (Object.keys(terminal).length === 0) {
54
+ throw runtimeError(
55
+ "CC_GRAPH_TERMINAL_EVIDENCE_REQUIRED",
56
+ "Graph node executor did not return immutable terminal evidence",
57
+ );
58
+ }
59
+ return terminal;
60
+ }
61
+
62
+ function usage(value) {
63
+ const source = value?.usage || {};
64
+ return {
65
+ turns: Math.max(0, Number(source.turns) || 1),
66
+ tokens: Math.max(
67
+ 0,
68
+ Number(source.tokens) ||
69
+ Number(source.input_tokens || 0) + Number(source.output_tokens || 0),
70
+ ),
71
+ costUsd: Math.max(0, Number(source.costUsd) || 0),
72
+ wallMs: Math.max(0, Number(source.wallMs) || 0),
73
+ };
74
+ }
75
+
76
+ function nodeInput(inputs, nodeId) {
77
+ const value = inputs?.[nodeId];
78
+ if (typeof value === "string") return { prompt: value };
79
+ if (value && typeof value === "object" && !Array.isArray(value)) {
80
+ return { ...value, prompt: String(value.prompt || value.input || "") };
81
+ }
82
+ return { prompt: "" };
83
+ }
84
+
85
+ function boundedInteger(value, fallback, { min = 0, max } = {}) {
86
+ if (value === undefined || value === null) return fallback;
87
+ const number = Number(value);
88
+ if (!Number.isSafeInteger(number) || number < min || number > max) {
89
+ throw runtimeError(
90
+ "CC_GRAPH_HISTORY_RANGE_INVALID",
91
+ `Graph history range must be an integer between ${min} and ${max}`,
92
+ );
93
+ }
94
+ return number;
95
+ }
96
+
97
+ function debuggerProjection(projection) {
98
+ return Object.freeze({
99
+ ...projection,
100
+ timeline: Object.freeze(
101
+ (projection.timeline || []).map((event) =>
102
+ Object.freeze({
103
+ seq: event.seq,
104
+ timestamp: event.timestamp,
105
+ type: event.type,
106
+ hash: event.hash,
107
+ }),
108
+ ),
109
+ ),
110
+ effects: Object.freeze(
111
+ (projection.effects || []).map((effect) =>
112
+ Object.freeze({
113
+ id: effect.id,
114
+ nodeId: effect.nodeId ?? null,
115
+ attemptId: effect.attemptId ?? null,
116
+ status: effect.status,
117
+ iterationPath: Object.freeze([...(effect.iterationPath || [])]),
118
+ receiptDigest: DIGEST.test(
119
+ String(effect.receipt?.receiptDigest || ""),
120
+ )
121
+ ? effect.receipt.receiptDigest
122
+ : null,
123
+ compensationEffectId: effect.compensationEffectId ?? null,
124
+ }),
125
+ ),
126
+ ),
127
+ });
128
+ }
129
+
130
+ /**
131
+ * Host-owned Graph executor used by fixed App Server capabilities. GraphKernel
132
+ * owns ready/attempt/effect/terminal state; the injected executor owns only a
133
+ * single real Agent turn and must return immutable evidence.
134
+ */
135
+ export class AppServerGraphRuntime {
136
+ constructor({
137
+ rolloutStore,
138
+ eventStore = rolloutStore
139
+ ? new GraphEventStore({ rolloutStore })
140
+ : new GraphEventStore(),
141
+ executeNode,
142
+ requestHumanTask = null,
143
+ now = Date.now,
144
+ createId = randomUUID,
145
+ onEvent = null,
146
+ writerLeaseTtlMs = 24 * 60 * 60 * 1000,
147
+ } = {}) {
148
+ if (typeof executeNode !== "function") {
149
+ throw new TypeError("AppServerGraphRuntime requires executeNode()");
150
+ }
151
+ this.eventStore = eventStore;
152
+ this.rolloutStore = eventStore.rolloutStore;
153
+ this.executeNode = executeNode;
154
+ this.requestHumanTask =
155
+ typeof requestHumanTask === "function" ? requestHumanTask : null;
156
+ this.now = now;
157
+ this.createId = createId;
158
+ this.onEvent = typeof onEvent === "function" ? onEvent : null;
159
+ this.writerLeaseTtlMs = writerLeaseTtlMs;
160
+ this.runs = new Map();
161
+ this.drives = new Map();
162
+ this.active = new Map();
163
+ }
164
+
165
+ _requestThread(runId) {
166
+ return `graph-request:${graphDigest(
167
+ runId,
168
+ "cc.app-server.graph-run-id/v1",
169
+ ).slice(7)}`;
170
+ }
171
+
172
+ _persistRequest(runId, request) {
173
+ const threadId = this._requestThread(runId);
174
+ this.rolloutStore.start({
175
+ threadId,
176
+ title: `Graph request ${runId}`,
177
+ metadata: { kind: "graph_run_request", graphRunId: runId },
178
+ });
179
+ const events = this.rolloutStore.read(threadId);
180
+ this.rolloutStore.append({
181
+ threadId,
182
+ eventType: "graph.request.committed",
183
+ idempotencyKey: `graph-request:${runId}`,
184
+ payload: request,
185
+ expectedRevision: events.at(-1)?.event_seq,
186
+ expectedHeadHash: events.at(-1)?.hash,
187
+ });
188
+ }
189
+
190
+ _readRequest(runId) {
191
+ let events;
192
+ try {
193
+ events = this.rolloutStore.read(this._requestThread(runId));
194
+ } catch (error) {
195
+ throw runtimeError(
196
+ "CC_GRAPH_REQUEST_RECEIPT_MISSING",
197
+ `Graph run has no durable request receipt: ${runId}`,
198
+ { cause: error },
199
+ );
200
+ }
201
+ const receipt = [...events]
202
+ .reverse()
203
+ .find((event) => event.event_type === "graph.request.committed");
204
+ if (!receipt?.payload?.definition || !receipt?.payload?.inputs) {
205
+ throw runtimeError(
206
+ "CC_GRAPH_REQUEST_RECEIPT_FORGED",
207
+ `Graph run request receipt is incomplete: ${runId}`,
208
+ );
209
+ }
210
+ return receipt.payload;
211
+ }
212
+
213
+ runtimeClaims() {
214
+ return Object.freeze({
215
+ originSurface: "desktop",
216
+ surface: "desktop",
217
+ execution: "real",
218
+ persistence: "durable",
219
+ isolated: true,
220
+ terminalEvidence: true,
221
+ durableHumanTasks: true,
222
+ humanTaskQuorum: true,
223
+ humanTaskSeparationOfDuties: true,
224
+ authorityModes: Object.freeze(["shadow", "canonical"]),
225
+ featureGated: true,
226
+ });
227
+ }
228
+
229
+ _emit(runId, type, payload = {}) {
230
+ try {
231
+ this.onEvent?.({ type, runId, ...payload });
232
+ } catch {
233
+ // Observability never owns execution authority.
234
+ }
235
+ }
236
+
237
+ _newKernel({ runId, generation = 1, authoritySource = "graph_kernel" }) {
238
+ const writerId = `app-server:${runId}:writer:${process.pid}:${generation}`;
239
+ const writerLeaseId = `app-server:${runId}:lease:${this.createId()}`;
240
+ const kernel = new GraphKernel({
241
+ eventStore: this.eventStore,
242
+ now: this.now,
243
+ writerId,
244
+ writerLeaseId,
245
+ authoritySource,
246
+ authorityGeneration: generation,
247
+ writerLeaseTtlMs: this.writerLeaseTtlMs,
248
+ });
249
+ return { kernel, writerId, writerLeaseId, generation };
250
+ }
251
+
252
+ start({
253
+ definition,
254
+ runId = this.createId(),
255
+ inputs = {},
256
+ originSurface = "desktop",
257
+ agentCapacity = 1,
258
+ authorityMode = "canonical",
259
+ } = {}) {
260
+ if (!["shadow", "canonical"].includes(authorityMode)) {
261
+ throw runtimeError(
262
+ "CC_GRAPH_AUTHORITY_MODE_UNSUPPORTED",
263
+ "App Server Graph execution accepts shadow or canonical authority",
264
+ );
265
+ }
266
+ const compiled = compileGraphDefinition(definition);
267
+ const id = String(runId);
268
+ const request = {
269
+ definition:
270
+ compiled.definitionMigration?.backupDefinition || compiled.definition,
271
+ inputs: { ...inputs },
272
+ originSurface,
273
+ agentCapacity: Math.max(1, Number(agentCapacity) || 1),
274
+ authorityMode,
275
+ };
276
+ this._persistRequest(id, request);
277
+ if (this.runs.has(id)) {
278
+ const existing = this.runs.get(id);
279
+ if (existing.compiled.revisionDigest !== compiled.revisionDigest) {
280
+ throw runtimeError(
281
+ "CC_GRAPH_RUN_CONFLICT",
282
+ "Graph run id is already bound to another revision",
283
+ );
284
+ }
285
+ return this.status(id);
286
+ }
287
+ let persisted = [];
288
+ try {
289
+ persisted = this.eventStore.read(id);
290
+ } catch (error) {
291
+ if (error?.code !== "CC_ROLLOUT_THREAD_NOT_FOUND") throw error;
292
+ }
293
+ if (persisted.length) {
294
+ const recovered = this._recover(id);
295
+ const projection = recovered.kernel.getRun(id);
296
+ if (projection.revisionDigest !== compiled.revisionDigest) {
297
+ throw runtimeError(
298
+ "CC_GRAPH_RUN_CONFLICT",
299
+ "persisted Graph run is bound to another revision",
300
+ );
301
+ }
302
+ if (projection.authorityMode !== authorityMode) {
303
+ throw runtimeError(
304
+ "CC_GRAPH_MIGRATION_REQUIRED",
305
+ "persisted Graph run authority mode cannot change without a migration saga",
306
+ );
307
+ }
308
+ recovered.compiled = compiled;
309
+ recovered.inputs = { ...inputs };
310
+ return projection;
311
+ }
312
+ const holder = this._newKernel({
313
+ runId: id,
314
+ authoritySource:
315
+ authorityMode === "shadow" ? "graph_kernel_shadow" : "graph_kernel",
316
+ });
317
+ const projection = holder.kernel.startRun(compiled, {
318
+ runId: id,
319
+ originSurface,
320
+ });
321
+ holder.kernel.registerAgent(id, {
322
+ agentId: "app-server-agent",
323
+ capacity: Math.max(1, Number(agentCapacity) || 1),
324
+ resident: true,
325
+ });
326
+ holder.kernel.sealRun(id);
327
+ this.runs.set(id, {
328
+ ...holder,
329
+ runId: id,
330
+ compiled,
331
+ inputs: { ...inputs },
332
+ });
333
+ this._emit(id, "graph/run-started", { projection });
334
+ return this.status(id);
335
+ }
336
+
337
+ run(options = {}) {
338
+ const projection = this.start(options);
339
+ if (projection.authorityMode === "shadow") {
340
+ return Promise.resolve(this.status(projection.id));
341
+ }
342
+ const settled = this._drive(projection.id);
343
+ if (options.waitForCompletion === true) return settled;
344
+ return Promise.resolve(projection);
345
+ }
346
+
347
+ resume(runId, { waitForCompletion = false } = {}) {
348
+ const id = String(runId || "").trim();
349
+ if (!id) {
350
+ throw runtimeError(
351
+ "CC_GRAPH_RUN_ID_REQUIRED",
352
+ "Graph resume requires a durable run id",
353
+ );
354
+ }
355
+ const request = this._readRequest(id);
356
+ return this.run({
357
+ ...request,
358
+ runId: id,
359
+ waitForCompletion: waitForCompletion === true,
360
+ });
361
+ }
362
+
363
+ _drive(runId) {
364
+ if (this.drives.has(runId)) return this.drives.get(runId);
365
+ const drive = this._driveLoop(runId).finally(() => {
366
+ this.drives.delete(runId);
367
+ });
368
+ drive.catch(() => {});
369
+ this.drives.set(runId, drive);
370
+ return drive;
371
+ }
372
+
373
+ async _driveLoop(runId) {
374
+ const entry = this.runs.get(runId);
375
+ if (!entry)
376
+ throw runtimeError("CC_GRAPH_RUN_NOT_FOUND", "Graph run missing");
377
+ const { kernel } = entry;
378
+ for (let steps = 0; steps < 10_000; steps += 1) {
379
+ const status = kernel.getRun(runId);
380
+ if (
381
+ TERMINAL.has(status.status) ||
382
+ status.status === "reconciliation_required"
383
+ ) {
384
+ this._emit(runId, "graph/run-settled", { projection: status });
385
+ return status;
386
+ }
387
+ const pendingHumanTask = kernel
388
+ .humanTasks(runId)
389
+ .find((task) => ["open", "claimed"].includes(task.status));
390
+ if (pendingHumanTask) {
391
+ if (!this.requestHumanTask) return status;
392
+ await this._requestHumanTaskDecision(entry, pendingHumanTask);
393
+ continue;
394
+ }
395
+ const next = kernel
396
+ .readyNodes(runId)
397
+ .find((candidate) => candidate.dispatch !== "subgraph");
398
+ if (!next) return status;
399
+ if (entry.compiled.nodes[next.nodeId]?.kind === "human") {
400
+ await this._executeHumanTask(entry, next.nodeId);
401
+ } else {
402
+ await this._executeAttempt(entry, next.nodeId);
403
+ }
404
+ }
405
+ throw runtimeError(
406
+ "CC_GRAPH_DRIVE_LIMIT",
407
+ "Graph executor exceeded its bounded scheduling step limit",
408
+ );
409
+ }
410
+
411
+ async _executeHumanTask(entry, nodeId) {
412
+ const { kernel } = entry;
413
+ const runId = entry.runId;
414
+ const node = entry.compiled.nodes[nodeId];
415
+ if (node.effectClass !== "none") {
416
+ throw runtimeError(
417
+ "CC_GRAPH_HUMAN_TASK_EFFECT_UNSUPPORTED",
418
+ "HumanTask nodes must use effectClass=none; approved effects belong to a dependent task node",
419
+ );
420
+ }
421
+ const attempt = kernel.assignNode(runId, nodeId, "app-server-agent", {
422
+ ttlMs: this.writerLeaseTtlMs,
423
+ leaseId: `app-server-human-attempt:${this.createId()}`,
424
+ grant: { executor: "durable_human_task", originSurface: "desktop" },
425
+ });
426
+ const quorum =
427
+ node.join === "quorum" ? Math.max(1, Number(node.quorum) || 1) : 1;
428
+ const task = kernel.createHumanTask(runId, {
429
+ humanTaskId: `app-server-human-task:${this.createId()}`,
430
+ attemptId: attempt.id,
431
+ leaseId: attempt.leaseId,
432
+ fence: attempt.fence,
433
+ operation: nodeInput(entry.inputs, nodeId),
434
+ nonce: `app-server-human-nonce:${this.createId()}`,
435
+ ttlMs: this.writerLeaseTtlMs,
436
+ quorum,
437
+ // Product quorum is intentionally fail-closed: one identity can never
438
+ // contribute more than one vote to a multi-review HumanTask.
439
+ separationOfDuties: quorum > 1,
440
+ });
441
+ this._emit(runId, "graph/human-task-requested", {
442
+ nodeId,
443
+ humanTaskId: task.id,
444
+ quorum: task.quorum,
445
+ separationOfDuties: task.separationOfDuties,
446
+ });
447
+ if (!this.requestHumanTask) {
448
+ return kernel.getRun(runId);
449
+ }
450
+ return this._requestHumanTaskDecision(entry, task);
451
+ }
452
+
453
+ async _requestHumanTaskDecision(entry, task) {
454
+ const { kernel, runId } = entry;
455
+ const response = await this.requestHumanTask({ task });
456
+ if (!response || typeof response !== "object" || Array.isArray(response)) {
457
+ throw runtimeError(
458
+ "CC_GRAPH_HUMAN_DECISION_INVALID",
459
+ "HumanTask client returned no structured decision",
460
+ );
461
+ }
462
+ for (const [field, expected] of [
463
+ ["humanTaskId", task.id],
464
+ ["runId", runId],
465
+ ["revisionDigest", task.revisionDigest],
466
+ ["operationDigest", task.operationDigest],
467
+ ["nonce", task.nonce],
468
+ ]) {
469
+ if (response[field] !== expected) {
470
+ throw runtimeError(
471
+ "CC_GRAPH_HUMAN_TASK_BINDING_MISMATCH",
472
+ `HumanTask client response does not match ${field}`,
473
+ );
474
+ }
475
+ }
476
+ const actorId = String(response.actorId || "").trim();
477
+ if (!actorId) {
478
+ throw runtimeError(
479
+ "CC_GRAPH_HUMAN_DECISION_INVALID",
480
+ "HumanTask client response requires an authenticated actorId",
481
+ );
482
+ }
483
+ if (
484
+ task.separationOfDuties === true &&
485
+ task.decisions.some((entry) => entry.actorId === actorId)
486
+ ) {
487
+ throw runtimeError(
488
+ "CC_GRAPH_HUMAN_SEPARATION_OF_DUTIES",
489
+ "HumanTask requires a different authenticated actor",
490
+ );
491
+ }
492
+ const claim = kernel.claimHumanTask(runId, task.id, actorId, {
493
+ claimLeaseId: `app-server-human-claim:${this.createId()}`,
494
+ ttlMs: Math.min(this.writerLeaseTtlMs, 5 * 60 * 1000),
495
+ });
496
+ const decided = kernel.decideHumanTask(runId, task.id, {
497
+ actorId,
498
+ claimLeaseId: claim.claimLeaseId,
499
+ revisionDigest: response.revisionDigest,
500
+ operationDigest: response.operationDigest,
501
+ nonce: response.nonce,
502
+ decision: response.decision,
503
+ });
504
+ this._emit(runId, "graph/human-task-decided", {
505
+ nodeId: decided.nodeId,
506
+ humanTaskId: decided.id,
507
+ actorId,
508
+ status: decided.status,
509
+ acceptedDecisions: decided.decisions.filter((entry) =>
510
+ ["acceptOnce", "acceptForTurn", "acceptForSession"].includes(
511
+ entry.decision.kind,
512
+ ),
513
+ ).length,
514
+ quorum: decided.quorum,
515
+ });
516
+ if (
517
+ decided.status !== "decided" ||
518
+ ["decline", "cancel"].includes(decided.decision?.kind)
519
+ ) {
520
+ return kernel.getRun(runId);
521
+ }
522
+ const settlement = kernel.assignNode(
523
+ runId,
524
+ decided.nodeId,
525
+ "app-server-agent",
526
+ {
527
+ ttlMs: this.writerLeaseTtlMs,
528
+ leaseId: `app-server-human-settlement:${this.createId()}`,
529
+ grant: {
530
+ executor: "durable_human_task_decision",
531
+ humanTaskId: decided.id,
532
+ },
533
+ },
534
+ );
535
+ const settled = kernel.settleAttempt(runId, {
536
+ attemptId: settlement.id,
537
+ leaseId: settlement.leaseId,
538
+ fence: settlement.fence,
539
+ outcome: "succeeded",
540
+ evidence: {
541
+ outputDigest: graphDigest(
542
+ {
543
+ humanTaskId: decided.id,
544
+ revisionDigest: decided.revisionDigest,
545
+ operationDigest: decided.operationDigest,
546
+ decisions: decided.decisions,
547
+ },
548
+ "cc.app-server.human-task-settlement/v1",
549
+ ),
550
+ },
551
+ usage: { turns: 0, tokens: 0, costUsd: 0, wallMs: 0 },
552
+ });
553
+ this._emit(runId, "graph/node-settled", {
554
+ nodeId: decided.nodeId,
555
+ attemptId: settlement.id,
556
+ outcome: "succeeded",
557
+ humanTaskId: decided.id,
558
+ });
559
+ return settled;
560
+ }
561
+
562
+ async _executeAttempt(entry, nodeId) {
563
+ const { kernel } = entry;
564
+ const runId = entry.runId;
565
+ const attempt = kernel.assignNode(runId, nodeId, "app-server-agent", {
566
+ ttlMs: this.writerLeaseTtlMs,
567
+ leaseId: `app-server-attempt:${this.createId()}`,
568
+ grant: { executor: "agent_kernel", originSurface: "desktop" },
569
+ });
570
+ let effect = null;
571
+ if (attempt.effectIdempotencyKey) {
572
+ effect = kernel.beginEffect(runId, {
573
+ effectId: `app-server-effect:${this.createId()}`,
574
+ attemptId: attempt.id,
575
+ leaseId: attempt.leaseId,
576
+ fence: attempt.fence,
577
+ idempotencyKey: attempt.effectIdempotencyKey,
578
+ operationDigest: graphDigest(
579
+ { runId, nodeId, input: nodeInput(entry.inputs, nodeId) },
580
+ "cc.app-server.graph-operation/v1",
581
+ ),
582
+ });
583
+ }
584
+ const controller = new AbortController();
585
+ let releaseActive;
586
+ const done = new Promise((resolve) => {
587
+ releaseActive = resolve;
588
+ });
589
+ this.active.set(attempt.id, { controller, done });
590
+ this._emit(runId, "graph/node-started", { nodeId, attemptId: attempt.id });
591
+ try {
592
+ const result = await this.executeNode({
593
+ runId,
594
+ nodeId,
595
+ attempt,
596
+ input: nodeInput(entry.inputs, nodeId),
597
+ signal: controller.signal,
598
+ });
599
+ if (!result || !["succeeded", "completed"].includes(result.status)) {
600
+ const error = runtimeError(
601
+ "CC_GRAPH_EXECUTOR_FAILED",
602
+ result?.error || "Graph node executor did not succeed",
603
+ );
604
+ error.outcomeKnown = result?.outcomeKnown === true;
605
+ throw error;
606
+ }
607
+ const receipt = graphExecutorReceipt(result);
608
+ if (controller.signal.aborted) return kernel.getRun(runId);
609
+ if (effect) {
610
+ kernel.settleEffect(runId, {
611
+ effectId: effect.id,
612
+ attemptId: attempt.id,
613
+ leaseId: attempt.leaseId,
614
+ fence: attempt.fence,
615
+ outcome: "committed",
616
+ receipt: { receiptDigest: receipt.receiptDigest },
617
+ });
618
+ }
619
+ const settled = kernel.settleAttempt(runId, {
620
+ attemptId: attempt.id,
621
+ leaseId: attempt.leaseId,
622
+ fence: attempt.fence,
623
+ outcome: "succeeded",
624
+ evidence: receipt.terminalEvidence,
625
+ usage: receipt.usage,
626
+ });
627
+ this._emit(runId, "graph/node-settled", {
628
+ nodeId,
629
+ attemptId: attempt.id,
630
+ outcome: "succeeded",
631
+ });
632
+ return settled;
633
+ } catch (error) {
634
+ if (controller.signal.aborted) return kernel.getRun(runId);
635
+ if (effect) {
636
+ const known = error?.outcomeKnown === true;
637
+ kernel.settleEffect(runId, {
638
+ effectId: effect.id,
639
+ attemptId: attempt.id,
640
+ leaseId: attempt.leaseId,
641
+ fence: attempt.fence,
642
+ outcome: known ? "failed" : "unknown",
643
+ });
644
+ if (!known) {
645
+ this._emit(runId, "graph/node-reconciliation-required", {
646
+ nodeId,
647
+ attemptId: attempt.id,
648
+ error: error?.message || String(error),
649
+ });
650
+ return kernel.getRun(runId);
651
+ }
652
+ }
653
+ const settled = kernel.settleAttempt(runId, {
654
+ attemptId: attempt.id,
655
+ leaseId: attempt.leaseId,
656
+ fence: attempt.fence,
657
+ outcome: "failed",
658
+ error: error?.message || String(error),
659
+ });
660
+ this._emit(runId, "graph/node-settled", {
661
+ nodeId,
662
+ attemptId: attempt.id,
663
+ outcome: "failed",
664
+ });
665
+ return settled;
666
+ } finally {
667
+ this.active.delete(attempt.id);
668
+ releaseActive();
669
+ }
670
+ }
671
+
672
+ status(runId) {
673
+ const id = String(runId);
674
+ const entry = this.runs.get(id) || this._recover(id);
675
+ return entry.kernel.getRun(id);
676
+ }
677
+
678
+ humanTasks(runId) {
679
+ const id = String(runId);
680
+ const entry = this.runs.get(id) || this._recover(id);
681
+ return entry.kernel.humanTasks(id);
682
+ }
683
+
684
+ history(
685
+ runId,
686
+ {
687
+ afterSeq = 0,
688
+ limit = DEFAULT_HISTORY_EVENT_LIMIT,
689
+ snapshotLimit = DEFAULT_HISTORY_SNAPSHOT_LIMIT,
690
+ } = {},
691
+ ) {
692
+ const id = String(runId);
693
+ const cursor = boundedInteger(afterSeq, 0, {
694
+ min: 0,
695
+ max: Number.MAX_SAFE_INTEGER,
696
+ });
697
+ const eventLimit = boundedInteger(limit, DEFAULT_HISTORY_EVENT_LIMIT, {
698
+ min: 1,
699
+ max: MAX_HISTORY_EVENT_LIMIT,
700
+ });
701
+ const projectionLimit = boundedInteger(
702
+ snapshotLimit,
703
+ DEFAULT_HISTORY_SNAPSHOT_LIMIT,
704
+ { min: 1, max: MAX_HISTORY_SNAPSHOT_LIMIT },
705
+ );
706
+ const head = this.eventStore.head(id);
707
+ const effectiveCursor =
708
+ cursor === 0 && head.seq > eventLimit ? head.seq - eventLimit : cursor;
709
+ const selected = this.eventStore.read(id, {
710
+ afterSeq: effectiveCursor,
711
+ limit: eventLimit + 1,
712
+ });
713
+ const hasMore = selected.length > eventLimit;
714
+ const events = selected.slice(0, eventLimit);
715
+ const stateEvents = events.filter(
716
+ (event) => event.payload?.state?.version === 1,
717
+ );
718
+ if (stateEvents.length === 0) {
719
+ throw runtimeError(
720
+ "CC_GRAPH_TRACE_STATE_MISSING",
721
+ `Graph history has no runtime snapshot after sequence ${effectiveCursor}: ${id}`,
722
+ );
723
+ }
724
+ const snapshotEvents = stateEvents.slice(-projectionLimit);
725
+ const snapshots = snapshotEvents.map((event) => {
726
+ const through = events.filter((candidate) => candidate.seq <= event.seq);
727
+ return Object.freeze({
728
+ seq: event.seq,
729
+ timestamp: event.timestamp,
730
+ type: event.type,
731
+ hash: event.hash,
732
+ projection: debuggerProjection(reduceGraphTrace(through)),
733
+ });
734
+ });
735
+ const diffs = snapshots
736
+ .slice(1)
737
+ .map((snapshot, index) =>
738
+ diffGraphTrace(snapshots[index].projection, snapshot.projection),
739
+ );
740
+ const current = snapshots.at(-1).projection;
741
+ const blockedRoots = Object.freeze(
742
+ current.taskGraph.nodes
743
+ .filter((node) => node.blockedRoot)
744
+ .map((node) =>
745
+ Object.freeze({
746
+ nodeId: node.id,
747
+ ...locateBlockedRoot(current, node.id),
748
+ }),
749
+ ),
750
+ );
751
+ return Object.freeze({
752
+ schema: "chainlesschain.graph-debug-history/v1",
753
+ runId: id,
754
+ requestedAfterSeq: cursor,
755
+ afterSeq: effectiveCursor,
756
+ nextAfterSeq: events.at(-1)?.seq || effectiveCursor,
757
+ hasMore,
758
+ truncatedBefore: effectiveCursor > cursor,
759
+ truncatedSnapshots: stateEvents.length > projectionLimit,
760
+ eventCount: events.length,
761
+ events: Object.freeze(
762
+ events.map((event) =>
763
+ Object.freeze({
764
+ seq: event.seq,
765
+ timestamp: event.timestamp,
766
+ type: event.type,
767
+ hash: event.hash,
768
+ }),
769
+ ),
770
+ ),
771
+ snapshots: Object.freeze(snapshots),
772
+ diffs: Object.freeze(diffs),
773
+ blockedRoots,
774
+ current,
775
+ });
776
+ }
777
+
778
+ _recover(runId) {
779
+ let events;
780
+ try {
781
+ events = this.eventStore.read(runId);
782
+ } catch (error) {
783
+ throw runtimeError(
784
+ "CC_GRAPH_RUN_NOT_FOUND",
785
+ `Graph run was not found: ${runId}`,
786
+ { cause: error },
787
+ );
788
+ }
789
+ const latest = events.at(-1);
790
+ const previous = [...events]
791
+ .reverse()
792
+ .find((event) => event.payload?.state?.authority)?.payload
793
+ .state.authority;
794
+ if (!previous || !latest) {
795
+ throw runtimeError(
796
+ "CC_GRAPH_RECOVERY_UNAVAILABLE",
797
+ `Graph run has no authority snapshot: ${runId}`,
798
+ );
799
+ }
800
+ const generation = Number(previous.authorityGeneration) + 1;
801
+ const authoritySource =
802
+ previous.authorityMode === "shadow"
803
+ ? "graph_kernel_shadow"
804
+ : "graph_kernel";
805
+ const holder = this._newKernel({
806
+ runId,
807
+ generation,
808
+ authoritySource,
809
+ });
810
+ const authority = createGraphAuthorityBinding({
811
+ ...previous,
812
+ authorityMode: previous.authorityMode,
813
+ authoritySource,
814
+ authorityGeneration: generation,
815
+ writerId: holder.writerId,
816
+ writerLeaseId: holder.writerLeaseId,
817
+ writerLeaseExpiresAt: new Date(
818
+ this.now() + this.writerLeaseTtlMs,
819
+ ).toISOString(),
820
+ eventHead: latest.hash,
821
+ });
822
+ holder.kernel.recoverRun(runId, { authority });
823
+ const request = this._readRequest(runId);
824
+ const entry = {
825
+ ...holder,
826
+ runId,
827
+ compiled: compileGraphDefinition(request.definition),
828
+ inputs: { ...request.inputs },
829
+ };
830
+ this.runs.set(runId, entry);
831
+ this._recoverReceiptedAttempts(entry);
832
+ return entry;
833
+ }
834
+
835
+ _executorReceipts(runId) {
836
+ let events;
837
+ try {
838
+ events = this.rolloutStore.read(`graph-executor:${runId}`);
839
+ } catch (error) {
840
+ if (error?.code === "CC_ROLLOUT_THREAD_NOT_FOUND") return new Map();
841
+ throw error;
842
+ }
843
+ const receipts = new Map();
844
+ for (const event of events) {
845
+ if (!event.event_type.startsWith("executor.")) continue;
846
+ const payload = event.payload || {};
847
+ if (
848
+ payload.runId !== runId ||
849
+ typeof payload.attemptId !== "string" ||
850
+ !["executor.succeeded", "executor.failed"].includes(event.event_type)
851
+ ) {
852
+ throw runtimeError(
853
+ "CC_GRAPH_EXECUTOR_RECEIPT_FORGED",
854
+ "Graph executor receipt does not match its run and attempt",
855
+ );
856
+ }
857
+ const status =
858
+ event.event_type === "executor.succeeded" ? "succeeded" : "failed";
859
+ if (
860
+ status === "succeeded" &&
861
+ !DIGEST.test(String(payload.outputDigest || ""))
862
+ ) {
863
+ throw runtimeError(
864
+ "CC_GRAPH_EXECUTOR_RECEIPT_FORGED",
865
+ "successful Graph executor receipt has no immutable output digest",
866
+ );
867
+ }
868
+ receipts.set(payload.attemptId, {
869
+ status,
870
+ nodeId: payload.nodeId,
871
+ outputDigest: payload.outputDigest,
872
+ error: payload.error,
873
+ receiptDigest: event.hash,
874
+ });
875
+ }
876
+ return receipts;
877
+ }
878
+
879
+ _latestState(runId) {
880
+ const event = [...this.eventStore.read(runId)]
881
+ .reverse()
882
+ .find((candidate) => candidate.payload?.state?.version === 1);
883
+ if (!event) {
884
+ throw runtimeError(
885
+ "CC_GRAPH_RECOVERY_UNAVAILABLE",
886
+ `Graph run has no recoverable state: ${runId}`,
887
+ );
888
+ }
889
+ return event.payload.state;
890
+ }
891
+
892
+ _recoverReceiptedAttempts(entry) {
893
+ const { kernel, runId } = entry;
894
+ const receipts = this._executorReceipts(runId);
895
+ if (receipts.size === 0) return kernel.getRun(runId);
896
+
897
+ let state = this._latestState(runId);
898
+ const attempts = new Map(state.attempts || []);
899
+ const receiptForAttempt = (attempt) => {
900
+ const visited = new Set();
901
+ let current = attempt;
902
+ while (current && !visited.has(current.id)) {
903
+ visited.add(current.id);
904
+ const receipt = receipts.get(current.id);
905
+ if (receipt) return receipt;
906
+ current = current.resumedFromAttemptId
907
+ ? attempts.get(current.resumedFromAttemptId)
908
+ : null;
909
+ }
910
+ return null;
911
+ };
912
+ const effectsByAttempt = new Map();
913
+ for (const [, effect] of state.effects || []) {
914
+ const values = effectsByAttempt.get(effect.attemptId) || [];
915
+ values.push(effect);
916
+ effectsByAttempt.set(effect.attemptId, values);
917
+ }
918
+ for (const [, attempt] of state.attempts || []) {
919
+ if (attempt.status !== "active") continue;
920
+ const receipt = receiptForAttempt(attempt);
921
+ if (!receipt || receipt.nodeId !== attempt.nodeId) continue;
922
+ const effects = effectsByAttempt.get(attempt.id) || [];
923
+ const expectedEffectStatus =
924
+ receipt.status === "succeeded" ? "committed" : "failed";
925
+ if (effects.some((effect) => effect.status !== expectedEffectStatus)) {
926
+ continue;
927
+ }
928
+ const authority = kernel.getRun(runId);
929
+ const settledAttempt =
930
+ attempt.authorityGeneration === authority.authorityGeneration &&
931
+ attempt.writerId === authority.writerId
932
+ ? attempt
933
+ : kernel.resumeAttempt(runId, attempt.id, {
934
+ resumedAttemptId: `app-server-recovery-attempt:${this.createId()}`,
935
+ leaseId: `app-server-recovery-lease:${this.createId()}`,
936
+ ttlMs: this.writerLeaseTtlMs,
937
+ reason:
938
+ "immutable executor receipt recovered after writer takeover",
939
+ });
940
+ kernel.settleAttempt(runId, {
941
+ attemptId: settledAttempt.id,
942
+ leaseId: settledAttempt.leaseId,
943
+ fence: settledAttempt.fence,
944
+ outcome: receipt.status,
945
+ evidence:
946
+ receipt.status === "succeeded"
947
+ ? { outputDigest: receipt.outputDigest }
948
+ : null,
949
+ error: receipt.error,
950
+ usage: { turns: 1 },
951
+ });
952
+ }
953
+
954
+ let projection = kernel.getRun(runId);
955
+ if (projection.status !== "reconciliation_required") return projection;
956
+ state = this._latestState(runId);
957
+ const effects = new Map(state.effects || []);
958
+ for (const effectId of projection.reconciliationEffectIds) {
959
+ const effect = effects.get(effectId);
960
+ const receipt = effect ? receipts.get(effect.attemptId) : null;
961
+ if (!effect || !receipt || receipt.nodeId !== effect.nodeId) continue;
962
+ const succeeded = receipt.status === "succeeded";
963
+ kernel.reconcileEffect(runId, {
964
+ effectId,
965
+ decision: succeeded ? "committed" : "failed",
966
+ receipt: succeeded ? { receiptDigest: receipt.receiptDigest } : null,
967
+ auditDecisionId: `executor-receipt:${receipt.receiptDigest.slice(7, 39)}`,
968
+ });
969
+ const retry = kernel.assignNode(
970
+ runId,
971
+ effect.nodeId,
972
+ "app-server-agent",
973
+ {
974
+ ttlMs: this.writerLeaseTtlMs,
975
+ leaseId: `app-server-recovery-attempt:${this.createId()}`,
976
+ grant: { executor: "agent_kernel_receipt_recovery" },
977
+ },
978
+ );
979
+ if (effect.idempotencyKey) {
980
+ kernel.beginEffect(runId, {
981
+ effectId: `app-server-recovery-effect:${this.createId()}`,
982
+ attemptId: retry.id,
983
+ leaseId: retry.leaseId,
984
+ fence: retry.fence,
985
+ idempotencyKey: effect.idempotencyKey,
986
+ operationDigest: effect.operationDigest,
987
+ });
988
+ }
989
+ kernel.settleAttempt(runId, {
990
+ attemptId: retry.id,
991
+ leaseId: retry.leaseId,
992
+ fence: retry.fence,
993
+ outcome: receipt.status,
994
+ evidence: succeeded ? { outputDigest: receipt.outputDigest } : null,
995
+ error: receipt.error,
996
+ usage: { turns: 1 },
997
+ });
998
+ projection = kernel.getRun(runId);
999
+ }
1000
+ return projection;
1001
+ }
1002
+
1003
+ async cancel(runId, reason = "cancelled by App Server client") {
1004
+ const id = String(runId);
1005
+ const entry = this.runs.get(id) || this._recover(id);
1006
+ return entry.kernel.cancelRun(id, {
1007
+ reason,
1008
+ interrupt: async (attempt) => {
1009
+ const active = this.active.get(attempt.id);
1010
+ if (!active) return;
1011
+ active.controller.abort(runtimeError("CC_GRAPH_CANCELLED", reason));
1012
+ await active.done;
1013
+ },
1014
+ });
1015
+ }
1016
+
1017
+ async reconcile(runId, input = {}) {
1018
+ const id = String(runId);
1019
+ const entry = this.runs.get(id) || this._recover(id);
1020
+ const effect = entry.kernel
1021
+ .effectState(id)
1022
+ .find((candidate) => candidate.id === input.effectId);
1023
+ if (!effect) {
1024
+ throw runtimeError(
1025
+ "CC_GRAPH_EFFECT_NOT_FOUND",
1026
+ `Graph reconciliation effect was not found: ${input.effectId}`,
1027
+ );
1028
+ }
1029
+ const terminalEvidence =
1030
+ input.decision === "committed"
1031
+ ? evidence({ terminalEvidence: input.terminalEvidence })
1032
+ : null;
1033
+ entry.kernel.reconcileEffect(id, input);
1034
+ if (input.decision === "committed") {
1035
+ const attempt = entry.kernel.assignNode(
1036
+ id,
1037
+ effect.nodeId,
1038
+ "app-server-agent",
1039
+ {
1040
+ ttlMs: this.writerLeaseTtlMs,
1041
+ leaseId: `app-server-reconcile-attempt:${this.createId()}`,
1042
+ grant: {
1043
+ executor: "audited_reconciliation_receipt",
1044
+ auditDecisionId: input.auditDecisionId,
1045
+ },
1046
+ },
1047
+ );
1048
+ entry.kernel.settleAttempt(id, {
1049
+ attemptId: attempt.id,
1050
+ leaseId: attempt.leaseId,
1051
+ fence: attempt.fence,
1052
+ outcome: "succeeded",
1053
+ evidence: terminalEvidence,
1054
+ usage: { turns: 1 },
1055
+ });
1056
+ const drive = this._drive(id);
1057
+ return input.waitForCompletion === true
1058
+ ? await drive
1059
+ : entry.kernel.getRun(id);
1060
+ }
1061
+ const drive = this._drive(id);
1062
+ return input.waitForCompletion === true
1063
+ ? await drive
1064
+ : entry.kernel.getRun(id);
1065
+ }
1066
+ }
1067
+
1068
+ export function graphExecutorReceipt(value) {
1069
+ const terminal = evidence(value);
1070
+ return Object.freeze({
1071
+ terminalEvidence: terminal,
1072
+ receiptDigest:
1073
+ value?.terminalEvidence?.eventDigest ||
1074
+ terminal.outputDigest ||
1075
+ graphDigest(terminal, "cc.app-server.graph-executor-receipt/v1"),
1076
+ usage: usage(value),
1077
+ });
1078
+ }