chainlesschain 0.165.9 → 0.166.0

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 (190) hide show
  1. package/completions/_cc +2 -1
  2. package/completions/cc.bash +2 -2
  3. package/completions/cc.fish +2 -2
  4. package/completions/cc.ps1 +2 -1
  5. package/package.json +2 -2
  6. package/src/assets/web-panel/assets/{AIOps-DxwjrnEA.js → AIOps-D5_pwQ7M.js} +1 -1
  7. package/src/assets/web-panel/assets/{ActionButton-JdbYowzn.js → ActionButton-DgoYO32D.js} +1 -1
  8. package/src/assets/web-panel/assets/{Analytics-C06GCahv.js → Analytics-BIpoZ4QH.js} +3 -3
  9. package/src/assets/web-panel/assets/{AppLayout-BUVEEyi1.js → AppLayout-BJ3XrCEe.js} +3 -3
  10. package/src/assets/web-panel/assets/{Artifacts-D_ORzkJd.js → Artifacts-DUFs_izZ.js} +1 -1
  11. package/src/assets/web-panel/assets/{Audit-8o_Y5ejG.js → Audit-BZdPwwfL.js} +1 -1
  12. package/src/assets/web-panel/assets/{BackgroundAgents-DrkHd5VU.js → BackgroundAgents-DK9Fn-8B.js} +1 -1
  13. package/src/assets/web-panel/assets/{Backup-CKBZCX11.js → Backup-Bd6bOhV_.js} +1 -1
  14. package/src/assets/web-panel/assets/{BaseInput-C8HkI399.js → BaseInput-CRnWlXrd.js} +1 -1
  15. package/src/assets/web-panel/assets/{Chat-zb2H2Wux.js → Chat-D1G_yEl6.js} +6 -6
  16. package/src/assets/web-panel/assets/{ChatBubbleRenderer-BDcNpU_F.js → ChatBubbleRenderer-DDAlEtm-.js} +1 -1
  17. package/src/assets/web-panel/assets/{Checkbox-MwPtQmNz.js → Checkbox-CP35SH7R.js} +1 -1
  18. package/src/assets/web-panel/assets/{Codegen-3p5QCkFK.js → Codegen-DugeL4pj.js} +1 -1
  19. package/src/assets/web-panel/assets/{Col-BPLlhn52.js → Col-DPuJsvKg.js} +1 -1
  20. package/src/assets/web-panel/assets/{Community-C-3dBNOM.js → Community-CZqNjNNp.js} +1 -1
  21. package/src/assets/web-panel/assets/{Compact-BUcy-F_b.js → Compact-eeyIPEAb.js} +1 -1
  22. package/src/assets/web-panel/assets/{Compliance-CI8NxT8m.js → Compliance-MGzTw87k.js} +1 -1
  23. package/src/assets/web-panel/assets/{Cowork-DaDEBS-Q.js → Cowork-D3goVIA5.js} +2 -2
  24. package/src/assets/web-panel/assets/{Cron-BSv9HXu5.js → Cron-9HA8hLLG.js} +2 -2
  25. package/src/assets/web-panel/assets/{Crosschain-yuiKlJmv.js → Crosschain-DCSOX3l6.js} +1 -1
  26. package/src/assets/web-panel/assets/{DID-D6LfldB0.js → DID-ji3lN5Bc.js} +2 -2
  27. package/src/assets/web-panel/assets/{Dashboard-CBhLqG61.js → Dashboard-DlZvF1E5.js} +2 -2
  28. package/src/assets/web-panel/assets/{Dropdown-BG8pIl3O.js → Dropdown-2FyZcow9.js} +1 -1
  29. package/src/assets/web-panel/assets/{EmailListRenderer-CY92VIE6.js → EmailListRenderer-qH2bSpSY.js} +1 -1
  30. package/src/assets/web-panel/assets/{FamilyGuardDashboard-1Q_ehauM.js → FamilyGuardDashboard-Bfx47wZj.js} +1 -1
  31. package/src/assets/web-panel/assets/{Federation-D3XirJIU.js → Federation-DhGcZbIv.js} +1 -1
  32. package/src/assets/web-panel/assets/{FormItemContext-DfI9UuGZ.js → FormItemContext-BMgi9_YQ.js} +1 -1
  33. package/src/assets/web-panel/assets/{GenericCardRenderer-IeCZk2m7.js → GenericCardRenderer-DugMac-4.js} +1 -1
  34. package/src/assets/web-panel/assets/{Git-DzK4IJAZ.js → Git-D943BpH-.js} +2 -2
  35. package/src/assets/web-panel/assets/{Governance-BHf5KfmQ.js → Governance-DZ66a8Aa.js} +1 -1
  36. package/src/assets/web-panel/assets/{Inference-BmaxSALS.js → Inference-B00Mp2tN.js} +1 -1
  37. package/src/assets/web-panel/assets/{KnowledgeGraph-BgePFLA9.js → KnowledgeGraph-BdwXXO2Q.js} +1 -1
  38. package/src/assets/web-panel/assets/{Logs-nqgxpOc4.js → Logs-_MVXyYWc.js} +2 -2
  39. package/src/assets/web-panel/assets/MarkdownRenderer-hOTeNyi7.js +1 -0
  40. package/src/assets/web-panel/assets/{Marketplace-Bu8to-86.js → Marketplace-CxeHRsnO.js} +1 -1
  41. package/src/assets/web-panel/assets/{McpTools-BFWTzEk0.js → McpTools-BOieTxZ4.js} +3 -3
  42. package/src/assets/web-panel/assets/{Memory-B026G2GA.js → Memory-BQD26CM3.js} +2 -2
  43. package/src/assets/web-panel/assets/{MobileBridge-CM2Eeg6I.js → MobileBridge-CPJ8ZlvR.js} +1 -1
  44. package/src/assets/web-panel/assets/{MobileProjects-B79Ob3hy.js → MobileProjects-D7qRVu0y.js} +1 -1
  45. package/src/assets/web-panel/assets/{Mtc-CxevSZuV.js → Mtc-BOyndGuk.js} +2 -2
  46. package/src/assets/web-panel/assets/{MtcAudit-X396cPEX.js → MtcAudit-CXQ_8ZoR.js} +4 -4
  47. package/src/assets/web-panel/assets/{Multisig-HWIB_aZ1.js → Multisig-32Oh9KbH.js} +3 -3
  48. package/src/assets/web-panel/assets/{NLProgramming-gnYD1Xwy.js → NLProgramming-CFAd00CX.js} +1 -1
  49. package/src/assets/web-panel/assets/{Notes-Dn2Gf_J1.js → Notes-BBCY_qJA.js} +3 -3
  50. package/src/assets/web-panel/assets/{NotificationSettings-BRKBQ5rq.js → NotificationSettings-uaQ0PdmW.js} +1 -1
  51. package/src/assets/web-panel/assets/{OrderTableRenderer-Dy___Cgo.js → OrderTableRenderer-kpBxJ-SG.js} +1 -1
  52. package/src/assets/web-panel/assets/{Organization-cqfccxnt.js → Organization-CmFn7Oeo.js} +4 -4
  53. package/src/assets/web-panel/assets/{Overflow-CSX4FHN3.js → Overflow-R_avat3a.js} +1 -1
  54. package/src/assets/web-panel/assets/{P2P-Bwa4aghp.js → P2P-BIhLFmkY.js} +2 -2
  55. package/src/assets/web-panel/assets/{PdhVaultBrowser-Dhszyjig.js → PdhVaultBrowser-DTJjk-_i.js} +3 -3
  56. package/src/assets/web-panel/assets/{Permissions-CXzTyk6e.js → Permissions-CkzYv8Yb.js} +4 -4
  57. package/src/assets/web-panel/assets/{PersonalDataHub-CFCAAJbb.js → PersonalDataHub--0QvN2Nz.js} +4 -4
  58. package/src/assets/web-panel/assets/{Pipeline-BPJbx6xt.js → Pipeline-Bfw5gQCH.js} +1 -1
  59. package/src/assets/web-panel/assets/{Privacy-BbiXvEHz.js → Privacy-DicjKfax.js} +1 -1
  60. package/src/assets/web-panel/assets/{ProjectInit-B-s7-wSD.js → ProjectInit-DUl_ykBp.js} +2 -2
  61. package/src/assets/web-panel/assets/{ProjectSettings-Dyug206y.js → ProjectSettings-CPYDvN8a.js} +2 -2
  62. package/src/assets/web-panel/assets/{Projects-BtAI5Fwo.js → Projects-DnZ9_fAr.js} +1 -1
  63. package/src/assets/web-panel/assets/{Providers-JpIPmz6N.js → Providers-BX1jP31w.js} +1 -1
  64. package/src/assets/web-panel/assets/{QrScannerModal-CZdoN64A.js → QrScannerModal-COab5qUl.js} +1 -1
  65. package/src/assets/web-panel/assets/{QuickAsk-BwvnoRIT.js → QuickAsk-CdKQ_JZ0.js} +1 -1
  66. package/src/assets/web-panel/assets/{Recommend-ASMlMQrD.js → Recommend-BPFHg8pK.js} +1 -1
  67. package/src/assets/web-panel/assets/{RemoteSession-BiJzKFKi.js → RemoteSession-CqrfTJvO.js} +3 -3
  68. package/src/assets/web-panel/assets/{Reputation-C80TWLIO.js → Reputation-DM-l8Pb3.js} +1 -1
  69. package/src/assets/web-panel/assets/{Row-C1VNmjmX.js → Row-CKGBTeCw.js} +1 -1
  70. package/src/assets/web-panel/assets/{RssFeed-T2MyGUlG.js → RssFeed-Wb0YHIkc.js} +3 -3
  71. package/src/assets/web-panel/assets/{Search-DXM6O83L.js → Search-DiBjxKv-.js} +1 -1
  72. package/src/assets/web-panel/assets/{Security-nv69kU7W.js → Security-BAVEmbWP.js} +4 -4
  73. package/src/assets/web-panel/assets/{Services-AyHWe7Qj.js → Services-A_a3TIRn.js} +2 -2
  74. package/src/assets/web-panel/assets/{Skeleton-D6PWLxFI.js → Skeleton-CloHJ-I7.js} +1 -1
  75. package/src/assets/web-panel/assets/{Skills-BqVeOmNQ.js → Skills-CkvV5hKa.js} +1 -1
  76. package/src/assets/web-panel/assets/{Sla-BegIPl9M.js → Sla-BYozxxsV.js} +1 -1
  77. package/src/assets/web-panel/assets/{SpeechSettings-CKJn3xk-.js → SpeechSettings-CWianiZF.js} +1 -1
  78. package/src/assets/web-panel/assets/{SyncSettings-Th4cnAMl.js → SyncSettings-BR4G75do.js} +2 -2
  79. package/src/assets/web-panel/assets/{Tasks-BuKfBroh.js → Tasks-D6gyUVrM.js} +1 -1
  80. package/src/assets/web-panel/assets/{Templates-CMuWN8Ac.js → Templates-DbfYIRzT.js} +1 -1
  81. package/src/assets/web-panel/assets/{Tenant-C4je8E8Q.js → Tenant-BHutvukT.js} +1 -1
  82. package/src/assets/web-panel/assets/{Terminal-agJESOcO.js → Terminal-CYEiy2wJ.js} +2 -2
  83. package/src/assets/web-panel/assets/{TimelineRenderer-D2qDuVZ_.js → TimelineRenderer-D9s1AxfE.js} +1 -1
  84. package/src/assets/web-panel/assets/{Tokens-Cvmn99rH.js → Tokens-ij9vxnoy.js} +1 -1
  85. package/src/assets/web-panel/assets/{Trigger-D_QYIHH8.js → Trigger-Dq_DCVeQ.js} +1 -1
  86. package/src/assets/web-panel/assets/{Trust-BmzvQLnX.js → Trust-Bs6wdfoH.js} +1 -1
  87. package/src/assets/web-panel/assets/{UkeySign-D_LopHwH.js → UkeySign-CLazyr5t.js} +1 -1
  88. package/src/assets/web-panel/assets/{VideoEditing-DoWl3PhQ.js → VideoEditing-viWotIHA.js} +1 -1
  89. package/src/assets/web-panel/assets/{Wallet-5NYcAkxe.js → Wallet-DlZ-A78d.js} +4 -4
  90. package/src/assets/web-panel/assets/{WebAuthn-B-_Q3ewp.js → WebAuthn-DSmOND3Y.js} +3 -3
  91. package/src/assets/web-panel/assets/{WorkflowEditor-CHET-gi2.js → WorkflowEditor-BempLOJi.js} +1 -1
  92. package/src/assets/web-panel/assets/{chat-C6nvb-n6.js → chat-CyJzf4JI.js} +1 -1
  93. package/src/assets/web-panel/assets/{colors-CTDE9LTX.js → colors-C62q7WrL.js} +1 -1
  94. package/src/assets/web-panel/assets/{compact-item-KnemdF8D.js → compact-item-7dYM1KW3.js} +1 -1
  95. package/src/assets/web-panel/assets/{createContext-DUJR3Uek.js → createContext-CYTy7z2Q.js} +1 -1
  96. package/src/assets/web-panel/assets/devWarning-BdTg_AMF.js +1 -0
  97. package/src/assets/web-panel/assets/{hasIn-CFNTaby8.js → hasIn-DHoclUqm.js} +1 -1
  98. package/src/assets/web-panel/assets/{index-65HO56Bv.js → index-2C8Xx2UO.js} +1 -1
  99. package/src/assets/web-panel/assets/{index-ox6mIwE2.js → index-3pI7AfmD.js} +1 -1
  100. package/src/assets/web-panel/assets/{index-JGTiDeOW.js → index-4gWv-Ubg.js} +1 -1
  101. package/src/assets/web-panel/assets/{index-rVDD7AAR.js → index-94ktimZG.js} +1 -1
  102. package/src/assets/web-panel/assets/{index-OmuhFYKE.js → index-9uR7rc9a.js} +1 -1
  103. package/src/assets/web-panel/assets/index-B6WvFV4N.js +1 -0
  104. package/src/assets/web-panel/assets/index-BKgfptzA.js +1 -0
  105. package/src/assets/web-panel/assets/{index-CquKnw5r.js → index-BMRmxFU_.js} +1 -1
  106. package/src/assets/web-panel/assets/{index-DzReoLyh.js → index-BMxvWauP.js} +1 -1
  107. package/src/assets/web-panel/assets/{index-DP-PbO11.js → index-BghdJ87W.js} +1 -1
  108. package/src/assets/web-panel/assets/{index-CSD9_I2_.js → index-Bzz0aJ86.js} +1 -1
  109. package/src/assets/web-panel/assets/{index-DF1Y0Vuw.js → index-C2efftJb.js} +1 -1
  110. package/src/assets/web-panel/assets/{index-B8RtbSkV.js → index-C5YWYlW9.js} +1 -1
  111. package/src/assets/web-panel/assets/{index-CHfUdnJH.js → index-CVTnruDt.js} +1 -1
  112. package/src/assets/web-panel/assets/{index-BwZM1rod.js → index-CW86F2Zw.js} +1 -1
  113. package/src/assets/web-panel/assets/{index-CyEtHvEE.js → index-CZRBvN6i.js} +1 -1
  114. package/src/assets/web-panel/assets/{index-Ceu92a8D.js → index-CjeViaU-.js} +1 -1
  115. package/src/assets/web-panel/assets/{index-DJyPeBBk.js → index-CnEZ4ZHj.js} +3 -3
  116. package/src/assets/web-panel/assets/{index-BdiC4LOs.js → index-D6BC4mmz.js} +1 -1
  117. package/src/assets/web-panel/assets/{index-k6uorYmf.js → index-DA-_807C.js} +1 -1
  118. package/src/assets/web-panel/assets/{index-DqPpJkte.js → index-DAYanuSk.js} +1 -1
  119. package/src/assets/web-panel/assets/{index-NtAcnJYW.js → index-DHgSRimK.js} +1 -1
  120. package/src/assets/web-panel/assets/{index-Wlc7o_C8.js → index-DJzZaPlb.js} +1 -1
  121. package/src/assets/web-panel/assets/{index-CKvBF3Ov.js → index-DK84lAS0.js} +1 -1
  122. package/src/assets/web-panel/assets/{index-CcF0D7UZ.js → index-DKdv8cju.js} +1 -1
  123. package/src/assets/web-panel/assets/{index-CnLpuTAU.js → index-DNLD3nZY.js} +1 -1
  124. package/src/assets/web-panel/assets/{index-DZgCRMxk.js → index-DOFAwpUK.js} +1 -1
  125. package/src/assets/web-panel/assets/{index-v-mtxmWH.js → index-DYKwGrAn.js} +1 -1
  126. package/src/assets/web-panel/assets/{index-Cc7dbl_p.js → index-DghnbMlY.js} +1 -1
  127. package/src/assets/web-panel/assets/{index-BuizJo2E.js → index-DhJjifZe.js} +1 -1
  128. package/src/assets/web-panel/assets/{index-NUR0If7O.js → index-DkczsbiP.js} +1 -1
  129. package/src/assets/web-panel/assets/{index-u0h6QqVV.js → index-DvEtHpLc.js} +1 -1
  130. package/src/assets/web-panel/assets/{index-bxTPfdri.js → index-Dyq9tK5u.js} +1 -1
  131. package/src/assets/web-panel/assets/{index-DfJzS3Qa.js → index-LK1MZoBj.js} +1 -1
  132. package/src/assets/web-panel/assets/{index-DF-fcTvQ.js → index-NuNCstRO.js} +1 -1
  133. package/src/assets/web-panel/assets/{index-BXWg1crv.js → index-QXtrdrub.js} +1 -1
  134. package/src/assets/web-panel/assets/{index-BTetEUWd.js → index-XrsAHHQN.js} +1 -1
  135. package/src/assets/web-panel/assets/{index-BHqSqnW7.js → index-ZXYp76IB.js} +1 -1
  136. package/src/assets/web-panel/assets/{index-C96N9xeL.js → index-lHfNxiB5.js} +1 -1
  137. package/src/assets/web-panel/assets/{initDefaultProps-qEt8dI-t.js → initDefaultProps-CwCl5sFQ.js} +1 -1
  138. package/src/assets/web-panel/assets/{motion-DetKPWy-.js → motion-BFJFy70N.js} +1 -1
  139. package/src/assets/web-panel/assets/{move-BeNYeDV2.js → move-ByrtVGq8.js} +1 -1
  140. package/src/assets/web-panel/assets/{mtc-parser-C0q0MK_T.js → mtc-parser-BFiH1mT3.js} +1 -1
  141. package/src/assets/web-panel/assets/{omit-DLKkrKpE.js → omit-bdvgpivj.js} +1 -1
  142. package/src/assets/web-panel/assets/{pickAttrs-Bw1tWsY8.js → pickAttrs-BlfuuE3s.js} +1 -1
  143. package/src/assets/web-panel/assets/{placementArrow-DeesBLbd.js → placementArrow-t2SC3DMy.js} +1 -1
  144. package/src/assets/web-panel/assets/{responsiveObserve-bqLhxHhn.js → responsiveObserve-CoEXv2Li.js} +1 -1
  145. package/src/assets/web-panel/assets/{slide-DGCXRoQm.js → slide-BfXCf0Fu.js} +1 -1
  146. package/src/assets/web-panel/assets/{statusUtils-H-re3Egl.js → statusUtils-DNsdyOuS.js} +1 -1
  147. package/src/assets/web-panel/assets/{styleChecker-BXg4qGX_.js → styleChecker-9aHukmoG.js} +1 -1
  148. package/src/assets/web-panel/assets/{useFlexGapSupport-CKLb2KA6.js → useFlexGapSupport-dTkPxzHN.js} +1 -1
  149. package/src/assets/web-panel/assets/{useFs-CTAhHeyw.js → useFs-DINwfItP.js} +1 -1
  150. package/src/assets/web-panel/assets/{usePersonalDataHub-BQ4pDh5g.js → usePersonalDataHub-Bj_RriBV.js} +1 -1
  151. package/src/assets/web-panel/assets/{vnode-jrSK6j4j.js → vnode-BzRAZrTc.js} +1 -1
  152. package/src/assets/web-panel/assets/{zoom-fOQE24pw.js → zoom-CamPjkeK.js} +1 -1
  153. package/src/assets/web-panel/index.html +1 -1
  154. package/src/command-help-index.json +4 -4
  155. package/src/command-manifest.json +1 -1
  156. package/src/commands/agent.js +1 -1
  157. package/src/commands/graph.js +100 -0
  158. package/src/commands/mcp.js +49 -21
  159. package/src/commands/orchestrate.js +85 -18
  160. package/src/commands/serve.js +39 -0
  161. package/src/commands/team.js +2 -0
  162. package/src/data/changelog.json +26 -0
  163. package/src/generated/cc-agent-protocol.schema.json +1964 -0
  164. package/src/lib/agent-sandbox.js +4 -7
  165. package/src/lib/app-server/bounded-queue.js +87 -0
  166. package/src/lib/app-server/cli-agent-kernel-adapter.js +309 -0
  167. package/src/lib/app-server/protocol.js +125 -0
  168. package/src/lib/app-server/rollout-store.js +603 -0
  169. package/src/lib/app-server/server.js +922 -0
  170. package/src/lib/app-server/sqlite-rollout-store.js +315 -0
  171. package/src/lib/app-server/stdio-transport.js +118 -0
  172. package/src/lib/codex-app-server-adapter.js +249 -0
  173. package/src/lib/graph-kernel/adapters.js +238 -0
  174. package/src/lib/graph-kernel/compiler.js +911 -0
  175. package/src/lib/graph-kernel/eval.js +207 -0
  176. package/src/lib/graph-kernel/event-store.js +101 -0
  177. package/src/lib/graph-kernel/index.js +7 -0
  178. package/src/lib/graph-kernel/runtime.js +2660 -0
  179. package/src/lib/graph-kernel/trace-reducer.js +315 -0
  180. package/src/lib/graph-kernel/trigger-adapter.js +183 -0
  181. package/src/lib/orchestrator.js +2 -1
  182. package/src/lib/record-replay/index.js +1 -0
  183. package/src/lib/record-replay/skill-recorder.js +386 -0
  184. package/src/lib/webhook-security.js +159 -0
  185. package/src/repl/agent-repl.js +5 -4
  186. package/src/repl/prompt-interactions.js +11 -1
  187. package/src/assets/web-panel/assets/MarkdownRenderer-xkX6izBt.js +0 -1
  188. package/src/assets/web-panel/assets/devWarning-1DqLH1KX.js +0 -1
  189. package/src/assets/web-panel/assets/index-BhJtcPWo.js +0 -1
  190. package/src/assets/web-panel/assets/index-Cq8MWPDT.js +0 -1
@@ -0,0 +1,911 @@
1
+ import { createHash } from "node:crypto";
2
+ import { readFileSync } from "node:fs";
3
+ import { validate } from "../json-schema-validate.js";
4
+
5
+ const graphProtocolSchema = JSON.parse(
6
+ readFileSync(
7
+ new URL("../../generated/cc-agent-protocol.schema.json", import.meta.url),
8
+ "utf8",
9
+ ),
10
+ );
11
+
12
+ export const GRAPH_DEFINITION_VERSION = 1;
13
+ export const GRAPH_DEFINITION_MIN_VERSION = 1;
14
+ export const GRAPH_COMPILE_ERROR = "CC_GRAPH_COMPILE_FAILED";
15
+
16
+ const compiledGraphs = new WeakSet();
17
+ const REFERENCE_PATTERNS = Object.freeze([
18
+ /\$\{node\.(.+?)\.output\.([A-Za-z0-9][A-Za-z0-9._:/-]*)[^}]*\}/gu,
19
+ /\$\{step\.([^.}]+)\.([A-Za-z0-9][A-Za-z0-9._:/-]*)[^}]*\}/gu,
20
+ ]);
21
+ const BUDGET_FIELDS = Object.freeze([
22
+ "turns",
23
+ "tokens",
24
+ "costUsd",
25
+ "wallMs",
26
+ "spawnCount",
27
+ ]);
28
+ const GRAPH_DEFINITION_SCHEMA = Object.freeze({
29
+ ...graphProtocolSchema.$defs.GraphDefinition,
30
+ $defs: graphProtocolSchema.$defs,
31
+ });
32
+
33
+ function stableValue(value, state = { seen: new WeakSet(), nodes: 0 }) {
34
+ state.nodes += 1;
35
+ if (state.nodes > 250_000) {
36
+ throw new TypeError("graph definition exceeds canonicalization limits");
37
+ }
38
+ if (Array.isArray(value))
39
+ return value.map((item) => stableValue(item, state));
40
+ if (!value || typeof value !== "object") {
41
+ if (typeof value === "number" && !Number.isFinite(value)) {
42
+ throw new TypeError("graph definition contains a non-finite number");
43
+ }
44
+ if (["bigint", "function", "symbol", "undefined"].includes(typeof value)) {
45
+ throw new TypeError("graph definition contains a non-JSON value");
46
+ }
47
+ return value;
48
+ }
49
+ if (state.seen.has(value)) {
50
+ throw new TypeError("graph definition contains a cycle or repeated object");
51
+ }
52
+ state.seen.add(value);
53
+ const prototype = Object.getPrototypeOf(value);
54
+ if (prototype !== Object.prototype && prototype !== null) {
55
+ throw new TypeError("graph definition must contain plain JSON objects");
56
+ }
57
+ return Object.fromEntries(
58
+ Object.keys(value)
59
+ .filter((key) => value[key] !== undefined)
60
+ .sort()
61
+ .map((key) => [key, stableValue(value[key], state)]),
62
+ );
63
+ }
64
+
65
+ function deepFreeze(value) {
66
+ if (!value || typeof value !== "object" || Object.isFrozen(value))
67
+ return value;
68
+ for (const child of Object.values(value)) deepFreeze(child);
69
+ return Object.freeze(value);
70
+ }
71
+
72
+ export function canonicalGraphJson(value) {
73
+ return JSON.stringify(stableValue(value));
74
+ }
75
+
76
+ export function graphDigest(value, domain = "cc.graph.definition/v1") {
77
+ return `sha256:${createHash("sha256")
78
+ .update(domain)
79
+ .update("\0")
80
+ .update(canonicalGraphJson(value))
81
+ .digest("hex")}`;
82
+ }
83
+
84
+ export class GraphCompileError extends Error {
85
+ constructor(diagnostics) {
86
+ const sorted = [...diagnostics].sort(
87
+ (left, right) =>
88
+ left.path.localeCompare(right.path) ||
89
+ left.code.localeCompare(right.code),
90
+ );
91
+ super(
92
+ `Graph compilation failed: ${sorted
93
+ .slice(0, 8)
94
+ .map((item) => `${item.code} at ${item.path}`)
95
+ .join("; ")}`,
96
+ );
97
+ this.name = "GraphCompileError";
98
+ this.code = GRAPH_COMPILE_ERROR;
99
+ this.diagnostics = Object.freeze(sorted.map(Object.freeze));
100
+ this.effectStarted = false;
101
+ }
102
+ }
103
+
104
+ function diagnostic(diagnostics, code, path, message, details = {}) {
105
+ diagnostics.push({ code, path, message, ...details });
106
+ }
107
+
108
+ function duplicateValues(values) {
109
+ const seen = new Set();
110
+ const duplicates = new Set();
111
+ for (const value of values) {
112
+ if (seen.has(value)) duplicates.add(value);
113
+ seen.add(value);
114
+ }
115
+ return [...duplicates].sort();
116
+ }
117
+
118
+ function outgoingMap(nodeIds, dependencies) {
119
+ const output = new Map([...nodeIds].map((id) => [id, new Set()]));
120
+ for (const [nodeId, parents] of dependencies) {
121
+ for (const parent of parents) output.get(parent)?.add(nodeId);
122
+ }
123
+ return output;
124
+ }
125
+
126
+ function topologicalSort(nodeIds, dependencies, diagnostics) {
127
+ const indegree = new Map(
128
+ [...nodeIds].map((id) => [id, dependencies.get(id)?.size || 0]),
129
+ );
130
+ const outgoing = outgoingMap(nodeIds, dependencies);
131
+ const ready = [...nodeIds].filter((id) => indegree.get(id) === 0).sort();
132
+ const order = [];
133
+ while (ready.length) {
134
+ const id = ready.shift();
135
+ order.push(id);
136
+ for (const child of [...(outgoing.get(id) || [])].sort()) {
137
+ indegree.set(child, indegree.get(child) - 1);
138
+ if (indegree.get(child) === 0) {
139
+ ready.push(child);
140
+ ready.sort();
141
+ }
142
+ }
143
+ }
144
+ if (order.length !== nodeIds.size) {
145
+ const cycleNodes = [...nodeIds].filter((id) => !order.includes(id)).sort();
146
+ diagnostic(
147
+ diagnostics,
148
+ "GRAPH_DEPENDENCY_CYCLE",
149
+ "#/nodes",
150
+ `task dependency graph contains a cycle: ${cycleNodes.join(", ")}`,
151
+ { nodeIds: cycleNodes },
152
+ );
153
+ }
154
+ return { order, outgoing };
155
+ }
156
+
157
+ function transitiveSets(order, dependencies, outgoing) {
158
+ const ancestors = new Map(
159
+ [...dependencies.keys()].map((id) => [id, new Set()]),
160
+ );
161
+ for (const id of order) {
162
+ const set = ancestors.get(id);
163
+ for (const parent of dependencies.get(id) || []) {
164
+ set.add(parent);
165
+ for (const ancestor of ancestors.get(parent) || []) set.add(ancestor);
166
+ }
167
+ }
168
+ const descendants = new Map(
169
+ [...dependencies.keys()].map((id) => [id, new Set()]),
170
+ );
171
+ for (const id of [...order].reverse()) {
172
+ const set = descendants.get(id);
173
+ for (const child of outgoing.get(id) || []) {
174
+ set.add(child);
175
+ for (const descendant of descendants.get(child) || [])
176
+ set.add(descendant);
177
+ }
178
+ }
179
+ return { ancestors, descendants };
180
+ }
181
+
182
+ function portMap(node, field, diagnostics, index) {
183
+ const ports = new Map();
184
+ const values = node[field] || [];
185
+ for (const [portIndex, port] of values.entries()) {
186
+ if (ports.has(port.name)) {
187
+ diagnostic(
188
+ diagnostics,
189
+ "GRAPH_DUPLICATE_PORT",
190
+ `#/nodes/${index}/${field}/${portIndex}/name`,
191
+ `duplicate ${field} port: ${port.name}`,
192
+ );
193
+ }
194
+ ports.set(port.name, port);
195
+ }
196
+ return ports;
197
+ }
198
+
199
+ function schemaTypeSet(schema) {
200
+ if (!schema || Object.keys(schema).length === 0) return null;
201
+ const type = schema.type;
202
+ if (Array.isArray(type)) return new Set(type);
203
+ if (typeof type === "string") return new Set([type]);
204
+ if (schema.const !== undefined) {
205
+ if (schema.const === null) return new Set(["null"]);
206
+ if (Array.isArray(schema.const)) return new Set(["array"]);
207
+ return new Set([typeof schema.const]);
208
+ }
209
+ return null;
210
+ }
211
+
212
+ export function isPortSchemaAssignable(producer, consumer) {
213
+ if (!consumer || Object.keys(consumer).length === 0) return true;
214
+ if (canonicalGraphJson(producer) === canonicalGraphJson(consumer))
215
+ return true;
216
+ const sourceTypes = schemaTypeSet(producer);
217
+ const targetTypes = schemaTypeSet(consumer);
218
+ if (
219
+ sourceTypes &&
220
+ targetTypes &&
221
+ [...sourceTypes].some((type) => !targetTypes.has(type))
222
+ ) {
223
+ return false;
224
+ }
225
+ if (Array.isArray(producer?.enum) && Array.isArray(consumer?.enum)) {
226
+ const allowed = new Set(consumer.enum.map((item) => JSON.stringify(item)));
227
+ if (producer.enum.some((item) => !allowed.has(JSON.stringify(item))))
228
+ return false;
229
+ }
230
+ if (
231
+ producer?.type === "object" &&
232
+ consumer?.type === "object" &&
233
+ consumer.properties
234
+ ) {
235
+ const sourceProperties = producer.properties || {};
236
+ for (const required of consumer.required || []) {
237
+ if (!Object.hasOwn(sourceProperties, required)) return false;
238
+ }
239
+ for (const [name, target] of Object.entries(consumer.properties)) {
240
+ if (
241
+ Object.hasOwn(sourceProperties, name) &&
242
+ !isPortSchemaAssignable(sourceProperties[name], target)
243
+ ) {
244
+ return false;
245
+ }
246
+ }
247
+ }
248
+ if (producer?.type === "array" && consumer?.type === "array") {
249
+ return isPortSchemaAssignable(producer.items || {}, consumer.items || {});
250
+ }
251
+ return true;
252
+ }
253
+
254
+ function normalizeScope(value) {
255
+ return String(value || "")
256
+ .replaceAll("\\", "/")
257
+ .replace(/^\.\//u, "")
258
+ .replace(/\/{2,}/gu, "/")
259
+ .replace(/\/$/u, "");
260
+ }
261
+
262
+ function staticScopePrefix(value) {
263
+ const scope = normalizeScope(value);
264
+ const wildcard = scope.search(/[?*[\]{}]/u);
265
+ return (wildcard < 0 ? scope : scope.slice(0, wildcard)).replace(/\/$/u, "");
266
+ }
267
+
268
+ export function writeScopesOverlap(left, right) {
269
+ const a = staticScopePrefix(left);
270
+ const b = staticScopePrefix(right);
271
+ if (!a || !b) return true;
272
+ return a === b || a.startsWith(`${b}/`) || b.startsWith(`${a}/`);
273
+ }
274
+
275
+ function loopMultipliers(definition, diagnostics, nodeIds) {
276
+ const multiplier = new Map([...nodeIds].map((id) => [id, 1]));
277
+ const membership = new Map();
278
+ for (const [index, loop] of definition.loops.entries()) {
279
+ for (const endpoint of [loop.entryNodeId, loop.exitNodeId]) {
280
+ if (!nodeIds.has(endpoint)) {
281
+ diagnostic(
282
+ diagnostics,
283
+ "GRAPH_UNKNOWN_LOOP_ENDPOINT",
284
+ `#/loops/${index}`,
285
+ `loop endpoint does not exist: ${endpoint}`,
286
+ );
287
+ }
288
+ }
289
+ for (const nodeId of loop.nodeIds) {
290
+ if (!nodeIds.has(nodeId)) {
291
+ diagnostic(
292
+ diagnostics,
293
+ "GRAPH_UNKNOWN_LOOP_NODE",
294
+ `#/loops/${index}/nodeIds`,
295
+ `loop node does not exist: ${nodeId}`,
296
+ );
297
+ continue;
298
+ }
299
+ const owners = membership.get(nodeId) || [];
300
+ owners.push(loop.id);
301
+ membership.set(nodeId, owners);
302
+ multiplier.set(nodeId, multiplier.get(nodeId) * loop.maxIterations);
303
+ }
304
+ if (
305
+ !loop.nodeIds.includes(loop.entryNodeId) ||
306
+ !loop.nodeIds.includes(loop.exitNodeId)
307
+ ) {
308
+ diagnostic(
309
+ diagnostics,
310
+ "GRAPH_LOOP_BOUNDARY_OUTSIDE_REGION",
311
+ `#/loops/${index}`,
312
+ "loop entry and exit must be members of the region",
313
+ );
314
+ }
315
+ }
316
+ for (const [nodeId, owners] of membership) {
317
+ if (owners.length > 1) {
318
+ diagnostic(
319
+ diagnostics,
320
+ "GRAPH_OVERLAPPING_LOOP_REGION",
321
+ "#/loops",
322
+ `node belongs to overlapping loop regions: ${nodeId}`,
323
+ { nodeId, loopIds: owners },
324
+ );
325
+ }
326
+ }
327
+ return multiplier;
328
+ }
329
+
330
+ function budgetUpperBound(definition, multiplier, diagnostics) {
331
+ const total = Object.fromEntries(BUDGET_FIELDS.map((field) => [field, 0]));
332
+ for (const node of definition.nodes) {
333
+ for (const field of BUDGET_FIELDS) {
334
+ total[field] +=
335
+ Number(node.budget?.[field] || 0) * multiplier.get(node.id);
336
+ }
337
+ }
338
+ for (const field of BUDGET_FIELDS) {
339
+ const cap = definition.budget?.[field];
340
+ if (cap != null && total[field] > cap) {
341
+ diagnostic(
342
+ diagnostics,
343
+ "GRAPH_BUDGET_EXCEEDED",
344
+ `#/budget/${field}`,
345
+ `worst-case ${field} budget ${total[field]} exceeds cap ${cap}`,
346
+ { field, required: total[field], cap },
347
+ );
348
+ }
349
+ }
350
+ return total;
351
+ }
352
+
353
+ function validateSubgraphs(definition, nodeIds, options, diagnostics) {
354
+ const registry = options.subgraphs || new Map();
355
+ const calls = new Map();
356
+ for (const [index, call] of definition.subgraphCalls.entries()) {
357
+ if (!nodeIds.has(call.nodeId)) {
358
+ diagnostic(
359
+ diagnostics,
360
+ "GRAPH_UNKNOWN_SUBGRAPH_NODE",
361
+ `#/subgraphCalls/${index}/nodeId`,
362
+ `subgraph call node does not exist: ${call.nodeId}`,
363
+ );
364
+ }
365
+ const target =
366
+ registry instanceof Map
367
+ ? registry.get(call.definitionId)
368
+ : registry[call.definitionId];
369
+ if (target && target.revisionDigest !== call.revisionDigest) {
370
+ diagnostic(
371
+ diagnostics,
372
+ "GRAPH_SUBGRAPH_DIGEST_MISMATCH",
373
+ `#/subgraphCalls/${index}/revisionDigest`,
374
+ `subgraph digest does not match pinned revision: ${call.definitionId}`,
375
+ );
376
+ }
377
+ calls.set(call.nodeId, call);
378
+ }
379
+
380
+ const definitions = new Map();
381
+ definitions.set(definition.id, definition);
382
+ if (registry instanceof Map) {
383
+ for (const [id, value] of registry) {
384
+ definitions.set(id, value.definition || value);
385
+ }
386
+ } else {
387
+ for (const [id, value] of Object.entries(registry)) {
388
+ definitions.set(id, value.definition || value);
389
+ }
390
+ }
391
+ const visiting = new Set();
392
+ const visited = new Set();
393
+ const visit = (id, path = []) => {
394
+ if (visiting.has(id)) {
395
+ diagnostic(
396
+ diagnostics,
397
+ "GRAPH_SUBGRAPH_CALL_CYCLE",
398
+ "#/subgraphCalls",
399
+ `subgraph call cycle: ${[...path, id].join(" -> ")}`,
400
+ );
401
+ return;
402
+ }
403
+ if (visited.has(id)) return;
404
+ const current = definitions.get(id);
405
+ if (!current) return;
406
+ visiting.add(id);
407
+ for (const call of current.subgraphCalls || []) {
408
+ visit(call.definitionId, [...path, id]);
409
+ }
410
+ visiting.delete(id);
411
+ visited.add(id);
412
+ };
413
+ visit(definition.id);
414
+ return calls;
415
+ }
416
+
417
+ function migrateDefinition(input, options) {
418
+ const sourceVersion = Number(input?.schemaVersion);
419
+ if (sourceVersion === GRAPH_DEFINITION_VERSION) {
420
+ return { definition: stableValue(input), migratedFrom: null };
421
+ }
422
+ const upcaster = options.upcasters?.[sourceVersion];
423
+ if (
424
+ sourceVersion === GRAPH_DEFINITION_VERSION - 1 &&
425
+ typeof upcaster === "function"
426
+ ) {
427
+ const migrated = upcaster(stableValue(input));
428
+ if (Number(migrated?.schemaVersion) !== GRAPH_DEFINITION_VERSION) {
429
+ throw new TypeError("graph upcaster did not produce the current version");
430
+ }
431
+ return { definition: stableValue(migrated), migratedFrom: sourceVersion };
432
+ }
433
+ return { definition: stableValue(input), migratedFrom: null };
434
+ }
435
+
436
+ export function compileGraphDefinition(input, options = {}) {
437
+ const diagnostics = [];
438
+ let migrated;
439
+ try {
440
+ migrated = migrateDefinition(input, options);
441
+ } catch (error) {
442
+ throw new GraphCompileError([
443
+ {
444
+ code: "GRAPH_CANONICALIZATION_FAILED",
445
+ path: "#",
446
+ message: error.message,
447
+ },
448
+ ]);
449
+ }
450
+ const definition = migrated.definition;
451
+ const schemaResult = validate(definition, GRAPH_DEFINITION_SCHEMA);
452
+ for (const error of schemaResult.errors) {
453
+ diagnostic(
454
+ diagnostics,
455
+ "GRAPH_SCHEMA_INVALID",
456
+ error.instancePath || error.schemaPath || "#",
457
+ error.message || "graph schema is invalid",
458
+ );
459
+ }
460
+ if (!schemaResult.valid) throw new GraphCompileError(diagnostics);
461
+ if (
462
+ definition.schemaVersion < GRAPH_DEFINITION_MIN_VERSION ||
463
+ definition.schemaVersion > GRAPH_DEFINITION_VERSION
464
+ ) {
465
+ diagnostic(
466
+ diagnostics,
467
+ "GRAPH_SCHEMA_VERSION_UNSUPPORTED",
468
+ "#/schemaVersion",
469
+ `supported GraphDefinition versions are ${GRAPH_DEFINITION_MIN_VERSION}-${GRAPH_DEFINITION_VERSION}`,
470
+ );
471
+ }
472
+
473
+ for (const id of duplicateValues(definition.nodes.map((node) => node.id))) {
474
+ diagnostic(
475
+ diagnostics,
476
+ "GRAPH_DUPLICATE_NODE_ID",
477
+ "#/nodes",
478
+ `duplicate node id: ${id}`,
479
+ );
480
+ }
481
+ for (const id of duplicateValues(definition.edges.map((edge) => edge.id))) {
482
+ diagnostic(
483
+ diagnostics,
484
+ "GRAPH_DUPLICATE_EDGE_ID",
485
+ "#/edges",
486
+ `duplicate edge id: ${id}`,
487
+ );
488
+ }
489
+ for (const id of duplicateValues(definition.loops.map((loop) => loop.id))) {
490
+ diagnostic(
491
+ diagnostics,
492
+ "GRAPH_DUPLICATE_REGION_ID",
493
+ "#/loops",
494
+ `duplicate loop region id: ${id}`,
495
+ );
496
+ }
497
+ for (const id of duplicateValues(
498
+ (definition.triggers || []).map((item) => item.id),
499
+ )) {
500
+ diagnostic(
501
+ diagnostics,
502
+ "GRAPH_DUPLICATE_TRIGGER_ID",
503
+ "#/triggers",
504
+ `duplicate trigger binding id: ${id}`,
505
+ );
506
+ }
507
+ const declaredRegionIds = [
508
+ ...definition.loops.map((region) => region.id),
509
+ ...(definition.regions || []).map((region) => region.id),
510
+ ];
511
+ for (const id of duplicateValues(declaredRegionIds)) {
512
+ diagnostic(
513
+ diagnostics,
514
+ "GRAPH_DUPLICATE_REGION_ID",
515
+ "#/regions",
516
+ `duplicate region id: ${id}`,
517
+ );
518
+ }
519
+ const nodes = new Map(definition.nodes.map((node) => [node.id, node]));
520
+ const nodeIds = new Set(nodes.keys());
521
+ for (const [index, trigger] of (definition.triggers || []).entries()) {
522
+ if (!nodeIds.has(trigger.targetNodeId)) {
523
+ diagnostic(
524
+ diagnostics,
525
+ "GRAPH_UNKNOWN_TRIGGER_TARGET",
526
+ `#/triggers/${index}/targetNodeId`,
527
+ `trigger targets unknown node: ${trigger.targetNodeId}`,
528
+ );
529
+ }
530
+ }
531
+ const regions = new Map(
532
+ (definition.regions || []).map((region) => [region.id, region]),
533
+ );
534
+ for (const [index, region] of (definition.regions || []).entries()) {
535
+ for (const regionNodeId of region.nodeIds) {
536
+ if (!nodeIds.has(regionNodeId)) {
537
+ diagnostic(
538
+ diagnostics,
539
+ "GRAPH_UNKNOWN_REGION_NODE",
540
+ `#/regions/${index}/nodeIds`,
541
+ `region contains unknown node: ${regionNodeId}`,
542
+ );
543
+ }
544
+ }
545
+ for (const [field, regionNodeId] of [
546
+ ["entryNodeId", region.entryNodeId],
547
+ ["exitNodeId", region.exitNodeId],
548
+ ]) {
549
+ if (regionNodeId && !region.nodeIds.includes(regionNodeId)) {
550
+ diagnostic(
551
+ diagnostics,
552
+ "GRAPH_REGION_BOUNDARY_OUTSIDE_REGION",
553
+ `#/regions/${index}/${field}`,
554
+ `${field} must belong to region ${region.id}`,
555
+ );
556
+ }
557
+ }
558
+ if (region.parentRegionId && !regions.has(region.parentRegionId)) {
559
+ diagnostic(
560
+ diagnostics,
561
+ "GRAPH_UNKNOWN_PARENT_REGION",
562
+ `#/regions/${index}/parentRegionId`,
563
+ `unknown parent region: ${region.parentRegionId}`,
564
+ );
565
+ }
566
+ const parentPath = new Set([region.id]);
567
+ let parentId = region.parentRegionId;
568
+ while (parentId && regions.has(parentId)) {
569
+ if (parentPath.has(parentId)) {
570
+ diagnostic(
571
+ diagnostics,
572
+ "GRAPH_REGION_CYCLE",
573
+ `#/regions/${index}/parentRegionId`,
574
+ `region parent hierarchy contains a cycle at ${parentId}`,
575
+ );
576
+ break;
577
+ }
578
+ parentPath.add(parentId);
579
+ parentId = regions.get(parentId).parentRegionId;
580
+ }
581
+ }
582
+ const inputs = new Map();
583
+ const outputs = new Map();
584
+ const dependencies = new Map(
585
+ definition.nodes.map((node) => [node.id, new Set(node.dependsOn)]),
586
+ );
587
+ for (const [index, node] of definition.nodes.entries()) {
588
+ inputs.set(node.id, portMap(node, "inputs", diagnostics, index));
589
+ outputs.set(node.id, portMap(node, "outputs", diagnostics, index));
590
+ for (const dependency of node.dependsOn) {
591
+ if (!nodeIds.has(dependency)) {
592
+ diagnostic(
593
+ diagnostics,
594
+ "GRAPH_UNKNOWN_DEPENDENCY",
595
+ `#/nodes/${index}/dependsOn`,
596
+ `unknown dependency ${dependency} for ${node.id}`,
597
+ );
598
+ }
599
+ if (dependency === node.id) {
600
+ diagnostic(
601
+ diagnostics,
602
+ "GRAPH_SELF_DEPENDENCY",
603
+ `#/nodes/${index}/dependsOn`,
604
+ `node cannot depend on itself: ${node.id}`,
605
+ );
606
+ }
607
+ }
608
+ const allowedCapabilities = new Set(definition.allowedCapabilities || []);
609
+ for (const capability of node.capabilities || []) {
610
+ if (!allowedCapabilities.has(capability)) {
611
+ diagnostic(
612
+ diagnostics,
613
+ "GRAPH_CAPABILITY_ESCALATION",
614
+ `#/nodes/${index}/capabilities`,
615
+ `node requests capability outside graph authority: ${capability}`,
616
+ );
617
+ }
618
+ }
619
+ if (
620
+ ["workspace_write", "external"].includes(node.effectClass) &&
621
+ !node.idempotencyKey
622
+ ) {
623
+ diagnostic(
624
+ diagnostics,
625
+ "GRAPH_EFFECT_MISSING_IDEMPOTENCY_KEY",
626
+ `#/nodes/${index}/idempotencyKey`,
627
+ `effectful node requires an idempotency key: ${node.id}`,
628
+ );
629
+ }
630
+ if (
631
+ node.effectClass === "workspace_write" &&
632
+ node.workspaceIsolation !== "worktree" &&
633
+ (!Array.isArray(node.writeSet) || node.writeSet.length === 0)
634
+ ) {
635
+ diagnostic(
636
+ diagnostics,
637
+ "GRAPH_WRITE_SCOPE_REQUIRED",
638
+ `#/nodes/${index}/writeSet`,
639
+ `writable node must use a worktree or declare a write set: ${node.id}`,
640
+ );
641
+ }
642
+ if (node.join === "quorum" && !(node.quorum >= 1)) {
643
+ diagnostic(
644
+ diagnostics,
645
+ "GRAPH_INVALID_QUORUM",
646
+ `#/nodes/${index}/quorum`,
647
+ "quorum join requires a positive quorum",
648
+ );
649
+ }
650
+ }
651
+
652
+ const dependencyPolicies = new Map(
653
+ definition.nodes.map((node) => [
654
+ node.id,
655
+ new Map(node.dependsOn.map((dependency) => [dependency, "success"])),
656
+ ]),
657
+ );
658
+ for (const [index, edge] of definition.edges.entries()) {
659
+ const from = nodes.get(edge.from);
660
+ const to = nodes.get(edge.to);
661
+ if (!from) {
662
+ diagnostic(
663
+ diagnostics,
664
+ "GRAPH_UNKNOWN_EDGE_SOURCE",
665
+ `#/edges/${index}/from`,
666
+ `unknown edge source: ${edge.from}`,
667
+ );
668
+ }
669
+ if (!to) {
670
+ diagnostic(
671
+ diagnostics,
672
+ "GRAPH_UNKNOWN_EDGE_TARGET",
673
+ `#/edges/${index}/to`,
674
+ `unknown edge target: ${edge.to}`,
675
+ );
676
+ }
677
+ if (!from || !to) continue;
678
+ if (edge.kind !== "compensation") {
679
+ dependencies.get(to.id).add(from.id);
680
+ dependencyPolicies.get(to.id).set(from.id, edge.when);
681
+ }
682
+ if (edge.kind === "data") {
683
+ const sourcePort = outputs.get(from.id).get(edge.fromPort);
684
+ const targetPort = inputs.get(to.id).get(edge.toPort);
685
+ if (!sourcePort) {
686
+ diagnostic(
687
+ diagnostics,
688
+ "GRAPH_UNKNOWN_OUTPUT_PORT",
689
+ `#/edges/${index}/fromPort`,
690
+ `unknown output port ${edge.fromPort} on ${from.id}`,
691
+ );
692
+ }
693
+ if (!targetPort) {
694
+ diagnostic(
695
+ diagnostics,
696
+ "GRAPH_UNKNOWN_INPUT_PORT",
697
+ `#/edges/${index}/toPort`,
698
+ `unknown input port ${edge.toPort} on ${to.id}`,
699
+ );
700
+ }
701
+ if (
702
+ sourcePort &&
703
+ targetPort &&
704
+ !isPortSchemaAssignable(sourcePort.schema, targetPort.schema)
705
+ ) {
706
+ diagnostic(
707
+ diagnostics,
708
+ "GRAPH_PORT_TYPE_MISMATCH",
709
+ `#/edges/${index}`,
710
+ `${from.id}.${edge.fromPort} is not assignable to ${to.id}.${edge.toPort}`,
711
+ );
712
+ }
713
+ }
714
+ }
715
+
716
+ const { order, outgoing } = topologicalSort(
717
+ nodeIds,
718
+ dependencies,
719
+ diagnostics,
720
+ );
721
+ const { ancestors, descendants } = transitiveSets(
722
+ order,
723
+ dependencies,
724
+ outgoing,
725
+ );
726
+
727
+ for (const [index, node] of definition.nodes.entries()) {
728
+ for (const [inputName, binding] of Object.entries(
729
+ node.inputBindings || {},
730
+ )) {
731
+ for (const pattern of REFERENCE_PATTERNS) {
732
+ pattern.lastIndex = 0;
733
+ for (const match of binding.matchAll(pattern)) {
734
+ const [, sourceId, portName] = match;
735
+ if (!nodes.has(sourceId)) {
736
+ diagnostic(
737
+ diagnostics,
738
+ "GRAPH_UNKNOWN_DATA_REFERENCE",
739
+ `#/nodes/${index}/inputBindings/${inputName}`,
740
+ `input references unknown node: ${sourceId}`,
741
+ );
742
+ } else if (!ancestors.get(node.id)?.has(sourceId)) {
743
+ diagnostic(
744
+ diagnostics,
745
+ "GRAPH_REFERENCE_OUTSIDE_DEPENDENCY_CLOSURE",
746
+ `#/nodes/${index}/inputBindings/${inputName}`,
747
+ `input references ${sourceId} without a transitive dependency`,
748
+ );
749
+ } else if (!outputs.get(sourceId)?.has(portName)) {
750
+ diagnostic(
751
+ diagnostics,
752
+ "GRAPH_UNKNOWN_OUTPUT_PORT",
753
+ `#/nodes/${index}/inputBindings/${inputName}`,
754
+ `input references unknown output ${sourceId}.${portName}`,
755
+ );
756
+ }
757
+ }
758
+ }
759
+ }
760
+ }
761
+
762
+ for (let leftIndex = 0; leftIndex < definition.nodes.length; leftIndex += 1) {
763
+ const left = definition.nodes[leftIndex];
764
+ if (!["workspace_write", "external"].includes(left.effectClass)) continue;
765
+ for (
766
+ let rightIndex = leftIndex + 1;
767
+ rightIndex < definition.nodes.length;
768
+ rightIndex += 1
769
+ ) {
770
+ const right = definition.nodes[rightIndex];
771
+ if (!["workspace_write", "external"].includes(right.effectClass))
772
+ continue;
773
+ if (
774
+ ancestors.get(left.id)?.has(right.id) ||
775
+ ancestors.get(right.id)?.has(left.id)
776
+ ) {
777
+ continue;
778
+ }
779
+ if (
780
+ left.workspaceIsolation === "worktree" &&
781
+ right.workspaceIsolation === "worktree"
782
+ ) {
783
+ continue;
784
+ }
785
+ const overlap = (left.writeSet || []).some((leftScope) =>
786
+ (right.writeSet || []).some((rightScope) =>
787
+ writeScopesOverlap(leftScope, rightScope),
788
+ ),
789
+ );
790
+ if (
791
+ overlap ||
792
+ left.effectClass === "external" ||
793
+ right.effectClass === "external"
794
+ ) {
795
+ diagnostic(
796
+ diagnostics,
797
+ "GRAPH_PARALLEL_WRITE_CONFLICT",
798
+ "#/nodes",
799
+ `unordered effectful nodes require worktrees or disjoint scopes: ${left.id}, ${right.id}`,
800
+ { nodeIds: [left.id, right.id] },
801
+ );
802
+ }
803
+ }
804
+ }
805
+
806
+ const multiplier = loopMultipliers(definition, diagnostics, nodeIds);
807
+ const budget = budgetUpperBound(definition, multiplier, diagnostics);
808
+ const subgraphCalls = validateSubgraphs(
809
+ definition,
810
+ nodeIds,
811
+ options,
812
+ diagnostics,
813
+ );
814
+ if (diagnostics.length) throw new GraphCompileError(diagnostics);
815
+
816
+ const revisionDigest = graphDigest(
817
+ definition,
818
+ `cc.graph.definition/${definition.schemaVersion}`,
819
+ );
820
+ const compiled = {
821
+ schema: "chainlesschain.compiled-graph/v1",
822
+ schemaVersion: definition.schemaVersion,
823
+ definition: deepFreeze(definition),
824
+ definitionId: definition.id,
825
+ revision: definition.revision,
826
+ revisionDigest,
827
+ migratedFrom: migrated.migratedFrom,
828
+ nodes: deepFreeze(
829
+ Object.fromEntries([...nodes].map(([id, value]) => [id, value])),
830
+ ),
831
+ topologicalOrder: Object.freeze(order),
832
+ dependencies: deepFreeze(
833
+ Object.fromEntries(
834
+ [...dependencies].map(([id, values]) => [id, [...values].sort()]),
835
+ ),
836
+ ),
837
+ dependencyPolicies: deepFreeze(
838
+ Object.fromEntries(
839
+ [...dependencyPolicies].map(([id, values]) => [
840
+ id,
841
+ Object.fromEntries([...values].sort()),
842
+ ]),
843
+ ),
844
+ ),
845
+ ancestors: deepFreeze(
846
+ Object.fromEntries(
847
+ [...ancestors].map(([id, values]) => [id, [...values].sort()]),
848
+ ),
849
+ ),
850
+ descendants: deepFreeze(
851
+ Object.fromEntries(
852
+ [...descendants].map(([id, values]) => [id, [...values].sort()]),
853
+ ),
854
+ ),
855
+ loopMultipliers: deepFreeze(Object.fromEntries(multiplier)),
856
+ budgetUpperBound: deepFreeze(budget),
857
+ subgraphCalls: deepFreeze(Object.fromEntries(subgraphCalls)),
858
+ triggers: deepFreeze([...(definition.triggers || [])]),
859
+ regions: deepFreeze([...(definition.regions || [])]),
860
+ };
861
+ deepFreeze(compiled);
862
+ compiledGraphs.add(compiled);
863
+ return compiled;
864
+ }
865
+
866
+ export function isCompiledGraph(value) {
867
+ return compiledGraphs.has(value);
868
+ }
869
+
870
+ export function assertCompiledGraph(value) {
871
+ if (!isCompiledGraph(value)) {
872
+ const error = new TypeError(
873
+ "Graph effects require an authenticated compileGraphDefinition result",
874
+ );
875
+ error.code = "CC_GRAPH_NOT_COMPILED";
876
+ error.effectStarted = false;
877
+ throw error;
878
+ }
879
+ return value;
880
+ }
881
+
882
+ export function migrateGraphDefinition(input, options = {}) {
883
+ const { definition, migratedFrom } = migrateDefinition(input, options);
884
+ const compiled = compileGraphDefinition(definition, options);
885
+ return Object.freeze({
886
+ dryRun: options.dryRun !== false,
887
+ fromVersion: migratedFrom ?? definition.schemaVersion,
888
+ toVersion: compiled.schemaVersion,
889
+ definition: compiled.definition,
890
+ revisionDigest: compiled.revisionDigest,
891
+ backupRequired: migratedFrom != null,
892
+ });
893
+ }
894
+
895
+ export function executionAttemptId(nodeId, iterationPath = [], attempt = 1) {
896
+ if (!Number.isSafeInteger(attempt) || attempt < 1) {
897
+ throw new TypeError("attempt must be a positive integer");
898
+ }
899
+ const path = Array.isArray(iterationPath)
900
+ ? iterationPath.map((value) => {
901
+ const number = Number(value);
902
+ if (!Number.isSafeInteger(number) || number < 0) {
903
+ throw new TypeError(
904
+ "iterationPath must contain non-negative integers",
905
+ );
906
+ }
907
+ return number;
908
+ })
909
+ : [];
910
+ return `${nodeId}@${path.length ? path.join(".") : "root"}#${attempt}`;
911
+ }