chainlesschain 0.163.3 → 0.163.4

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 (198) hide show
  1. package/package.json +3 -2
  2. package/scripts/command-lifecycle-report.mjs +330 -38
  3. package/src/assets/web-panel/assets/{AIOps-DIY6KxV-.js → AIOps-DB5X7qqV.js} +1 -1
  4. package/src/assets/web-panel/assets/{ActionButton-BXH0muPt.js → ActionButton-DxHRtKLk.js} +1 -1
  5. package/src/assets/web-panel/assets/{Analytics-D39w8vmg.js → Analytics-DIH5QrxT.js} +2 -2
  6. package/src/assets/web-panel/assets/{AppLayout-CUU1zvLs.js → AppLayout-uu0oxVz7.js} +4 -4
  7. package/src/assets/web-panel/assets/{Artifacts-D8azHO3T.js → Artifacts-BNSMiW9Y.js} +1 -1
  8. package/src/assets/web-panel/assets/{Audit-D0tAwXTg.js → Audit-DcAzB4_x.js} +1 -1
  9. package/src/assets/web-panel/assets/{BackgroundAgents-C29mhJll.js → BackgroundAgents-ClSsvjIc.js} +1 -1
  10. package/src/assets/web-panel/assets/{Backup-D6Aa1Gro.js → Backup-BMciY0js.js} +1 -1
  11. package/src/assets/web-panel/assets/{BaseInput-B1uoHbPQ.js → BaseInput-Hn3b57Sh.js} +1 -1
  12. package/src/assets/web-panel/assets/{Chat-Bs4DLOR5.js → Chat-CoSlC53V.js} +5 -5
  13. package/src/assets/web-panel/assets/{ChatBubbleRenderer-DUYIBSle.js → ChatBubbleRenderer-CFevJ3So.js} +1 -1
  14. package/src/assets/web-panel/assets/{Checkbox-CNcBPEa4.js → Checkbox-B4L4Eu4w.js} +1 -1
  15. package/src/assets/web-panel/assets/{Codegen-CIY89763.js → Codegen-C77FgRxF.js} +1 -1
  16. package/src/assets/web-panel/assets/{Col-CsPYFmgo.js → Col-BrLtJLH3.js} +1 -1
  17. package/src/assets/web-panel/assets/{Community-BBocEge4.js → Community-Be7pXXhA.js} +1 -1
  18. package/src/assets/web-panel/assets/{Compact-TQ8Ux0Lf.js → Compact-DuGPecjo.js} +1 -1
  19. package/src/assets/web-panel/assets/{Compliance-DfBsZTft.js → Compliance-CzS66mjP.js} +1 -1
  20. package/src/assets/web-panel/assets/{Cowork-Cg-I6FVH.js → Cowork-CEhjCoyK.js} +2 -2
  21. package/src/assets/web-panel/assets/{Cron-DTQzfRTp.js → Cron-D7ir8Vja.js} +2 -2
  22. package/src/assets/web-panel/assets/{Crosschain-DGxEKGpR.js → Crosschain-BcXmuLxf.js} +1 -1
  23. package/src/assets/web-panel/assets/{DID-DLZ1RDSr.js → DID-Cto1dtBy.js} +2 -2
  24. package/src/assets/web-panel/assets/{Dashboard-XneICKNI.js → Dashboard-B7KjgwOX.js} +2 -2
  25. package/src/assets/web-panel/assets/{Dropdown-BRU00J1-.js → Dropdown-COMqxijg.js} +1 -1
  26. package/src/assets/web-panel/assets/{EmailListRenderer-BzCbJuAy.js → EmailListRenderer-BZrya-nf.js} +1 -1
  27. package/src/assets/web-panel/assets/{FamilyGuardDashboard-D7BRyNBG.js → FamilyGuardDashboard-AmVeMq0J.js} +1 -1
  28. package/src/assets/web-panel/assets/{Federation-D9DeFIhW.js → Federation-kZU8c-27.js} +1 -1
  29. package/src/assets/web-panel/assets/{FormItemContext-6rAtA9Ko.js → FormItemContext-sOV22v2-.js} +1 -1
  30. package/src/assets/web-panel/assets/{GenericCardRenderer-8Hqy80pU.js → GenericCardRenderer-BUuLtGCz.js} +1 -1
  31. package/src/assets/web-panel/assets/{Git-Cmvkj1rt.js → Git-K-ZxsLsH.js} +2 -2
  32. package/src/assets/web-panel/assets/{Governance-BRiQUjjw.js → Governance-D3OEnawI.js} +1 -1
  33. package/src/assets/web-panel/assets/{Inference-CKN_EzVf.js → Inference-Bll0gRmP.js} +1 -1
  34. package/src/assets/web-panel/assets/{KnowledgeGraph-BCDHwpwQ.js → KnowledgeGraph-DBXwjr53.js} +1 -1
  35. package/src/assets/web-panel/assets/{Logs-DK9ovhaB.js → Logs-CEzPwEfP.js} +2 -2
  36. package/src/assets/web-panel/assets/MarkdownRenderer-CLmCbHx6.js +1 -0
  37. package/src/assets/web-panel/assets/{Marketplace-BILPGakp.js → Marketplace-BYyRyOPz.js} +1 -1
  38. package/src/assets/web-panel/assets/{McpTools-DTVCOr9-.js → McpTools-DVu8w_rM.js} +3 -3
  39. package/src/assets/web-panel/assets/{Memory-Hzc1iow4.js → Memory-tenuZ_hO.js} +2 -2
  40. package/src/assets/web-panel/assets/{MobileBridge-GXfgnGYV.js → MobileBridge-Bxgy4ozS.js} +1 -1
  41. package/src/assets/web-panel/assets/{MobileProjects-B-3TJWsS.js → MobileProjects-Dgx1D6sc.js} +1 -1
  42. package/src/assets/web-panel/assets/{Mtc-DwSJMWtK.js → Mtc-CRfJ8r8j.js} +4 -4
  43. package/src/assets/web-panel/assets/{MtcAudit-D2uxf-DA.js → MtcAudit-BBJq4Aiz.js} +4 -4
  44. package/src/assets/web-panel/assets/{Multisig-DG6kbKK0.js → Multisig-B3ii9lai.js} +3 -3
  45. package/src/assets/web-panel/assets/{NLProgramming-f5p49EDU.js → NLProgramming-FIi-Cubv.js} +1 -1
  46. package/src/assets/web-panel/assets/{Notes-FUgHyzSD.js → Notes-DIq8MPf1.js} +3 -3
  47. package/src/assets/web-panel/assets/{NotificationSettings-DCHGee7e.js → NotificationSettings-BFOD9eGd.js} +1 -1
  48. package/src/assets/web-panel/assets/{OrderTableRenderer-Dm6LlIoa.js → OrderTableRenderer--41u7vre.js} +1 -1
  49. package/src/assets/web-panel/assets/{Organization-BawA7qAg.js → Organization-B_0AWztW.js} +4 -4
  50. package/src/assets/web-panel/assets/{Overflow-D6a5wZU1.js → Overflow-B6I7TuXv.js} +1 -1
  51. package/src/assets/web-panel/assets/{P2P-K8WavwnA.js → P2P-Cdf6qens.js} +2 -2
  52. package/src/assets/web-panel/assets/{PdhVaultBrowser-4JAJYaqx.js → PdhVaultBrowser-B_0FcQJY.js} +3 -3
  53. package/src/assets/web-panel/assets/{Permissions-BDWvi8PD.js → Permissions-6nFBjsQp.js} +3 -3
  54. package/src/assets/web-panel/assets/{PersonalDataHub-DF_K4E9i.js → PersonalDataHub-nQYV7xpn.js} +3 -3
  55. package/src/assets/web-panel/assets/{Pipeline-BYeZlesI.js → Pipeline-CBVod5jm.js} +1 -1
  56. package/src/assets/web-panel/assets/{Privacy-BIHlgnIc.js → Privacy-D52jW1Nt.js} +1 -1
  57. package/src/assets/web-panel/assets/{ProjectInit-Cvf-6B5P.js → ProjectInit-Bdx3BYhf.js} +2 -2
  58. package/src/assets/web-panel/assets/{ProjectSettings-CnwfJrRz.js → ProjectSettings-b7xr4DUa.js} +2 -2
  59. package/src/assets/web-panel/assets/{Projects-b-jPXzF4.js → Projects-DhfifCKx.js} +1 -1
  60. package/src/assets/web-panel/assets/Providers-Dy_PgDMr.js +1 -0
  61. package/src/assets/web-panel/assets/{QrScannerModal-VLf3gAfL.js → QrScannerModal-FQKSmc3e.js} +1 -1
  62. package/src/assets/web-panel/assets/{QuickAsk-B5vGjRiF.js → QuickAsk-CSTrhzcF.js} +1 -1
  63. package/src/assets/web-panel/assets/{Recommend--UW4iYs-.js → Recommend-CxMABOUh.js} +1 -1
  64. package/src/assets/web-panel/assets/{RemoteSession-DMSPcfKR.js → RemoteSession-Cv2T8yRa.js} +2 -2
  65. package/src/assets/web-panel/assets/{Reputation-kQXlEHmj.js → Reputation-CE1k4Vvu.js} +1 -1
  66. package/src/assets/web-panel/assets/{Row-CxGu3pit.js → Row-BNgNcAV_.js} +1 -1
  67. package/src/assets/web-panel/assets/{RssFeed-Bi5lo8MZ.js → RssFeed-5ZiBZ3ph.js} +3 -3
  68. package/src/assets/web-panel/assets/{Search-Cq_H5RTh.js → Search-mDOIF1UV.js} +1 -1
  69. package/src/assets/web-panel/assets/{Security-YFbmkaQS.js → Security-CtuCTC-e.js} +3 -3
  70. package/src/assets/web-panel/assets/{Services-DLOGdaLm.js → Services-DxFO2Zg7.js} +2 -2
  71. package/src/assets/web-panel/assets/{Skeleton-CteIxb3D.js → Skeleton-CKmKkH9y.js} +1 -1
  72. package/src/assets/web-panel/assets/{Skills-CccopaIZ.js → Skills-Bmzl9qkk.js} +1 -1
  73. package/src/assets/web-panel/assets/{Sla-AzfEQKX1.js → Sla-9RqaWHOC.js} +1 -1
  74. package/src/assets/web-panel/assets/{SpeechSettings-ekGo5l77.js → SpeechSettings-CGdzFIYw.js} +1 -1
  75. package/src/assets/web-panel/assets/{SyncSettings-BmZmAgYH.js → SyncSettings-CvNuCeBT.js} +2 -2
  76. package/src/assets/web-panel/assets/{Tasks-D7q3Z4uL.js → Tasks-B_sLdcnw.js} +1 -1
  77. package/src/assets/web-panel/assets/{Templates-CX1Tgeka.js → Templates-D9_YijdK.js} +1 -1
  78. package/src/assets/web-panel/assets/{Tenant-Zc59jF4i.js → Tenant-Cuc7BUSP.js} +1 -1
  79. package/src/assets/web-panel/assets/{Terminal-CXOD7y83.js → Terminal-6VYRnJgd.js} +2 -2
  80. package/src/assets/web-panel/assets/{TimelineRenderer-B1aBHPBo.js → TimelineRenderer-GtK8xd8Y.js} +1 -1
  81. package/src/assets/web-panel/assets/{Tokens-zALlOq_U.js → Tokens-BZ94Lt5W.js} +1 -1
  82. package/src/assets/web-panel/assets/{Trigger-CQfJMXaq.js → Trigger-DEcZgDaX.js} +1 -1
  83. package/src/assets/web-panel/assets/{Trust-BU0RcM-w.js → Trust-CC3z9THe.js} +1 -1
  84. package/src/assets/web-panel/assets/{UkeySign-IFZ3HsbH.js → UkeySign-Cvtw8cV3.js} +1 -1
  85. package/src/assets/web-panel/assets/{VideoEditing-BzslgW0v.js → VideoEditing-CL4e6Bn3.js} +1 -1
  86. package/src/assets/web-panel/assets/{Wallet-BZAcQNS6.js → Wallet-ycEemP27.js} +2 -2
  87. package/src/assets/web-panel/assets/{WebAuthn-CBK1DMi0.js → WebAuthn-DgZ4X_aw.js} +5 -5
  88. package/src/assets/web-panel/assets/{WorkflowEditor-BlMTPzO-.js → WorkflowEditor-D0qMhSv7.js} +1 -1
  89. package/src/assets/web-panel/assets/{chat-B51-8QVn.js → chat-DsAg01Zn.js} +1 -1
  90. package/src/assets/web-panel/assets/{colors-504RTByU.js → colors-B0oqAhrQ.js} +1 -1
  91. package/src/assets/web-panel/assets/{compact-item-C-GP6IQM.js → compact-item-CmX0Rabz.js} +1 -1
  92. package/src/assets/web-panel/assets/{createContext-DOkIj7ID.js → createContext-Be4cn1Ub.js} +1 -1
  93. package/src/assets/web-panel/assets/devWarning-CP2qzG6D.js +1 -0
  94. package/src/assets/web-panel/assets/{hasIn-DN7V-_FW.js → hasIn-D5v12kVq.js} +1 -1
  95. package/src/assets/web-panel/assets/{index-kt_5rfcZ.js → index-0eWLkQKT.js} +1 -1
  96. package/src/assets/web-panel/assets/{index-CUhQpiYv.js → index-301ogenv.js} +1 -1
  97. package/src/assets/web-panel/assets/{index-f4Bo4KXm.js → index-5LGY0JbG.js} +1 -1
  98. package/src/assets/web-panel/assets/{index--n-ZciV3.js → index-7VKDn-t1.js} +1 -1
  99. package/src/assets/web-panel/assets/{index-D1phyca8.js → index-8WvYGnON.js} +1 -1
  100. package/src/assets/web-panel/assets/{index-7jIqHbzw.js → index-B1YGjOWC.js} +1 -1
  101. package/src/assets/web-panel/assets/{index-DEMPUVa3.js → index-B3mUHuVc.js} +1 -1
  102. package/src/assets/web-panel/assets/{index-Bkxhaeta.js → index-BA4oulaP.js} +1 -1
  103. package/src/assets/web-panel/assets/{index-CYopNjcJ.js → index-BBREnn1V.js} +1 -1
  104. package/src/assets/web-panel/assets/{index-P2mfY27Z.js → index-BBvyHguy.js} +1 -1
  105. package/src/assets/web-panel/assets/{index-DA2f1UV3.js → index-BKmsS2jj.js} +1 -1
  106. package/src/assets/web-panel/assets/{index-CP2A226L.js → index-BRIqLy1m.js} +1 -1
  107. package/src/assets/web-panel/assets/{index-CfiEM64n.js → index-BXGSbelg.js} +3 -3
  108. package/src/assets/web-panel/assets/{index-B2-x9bS8.js → index-BaE0Ui07.js} +1 -1
  109. package/src/assets/web-panel/assets/{index-CySTJKeq.js → index-BoAIAZ5C.js} +1 -1
  110. package/src/assets/web-panel/assets/{index-kAqQXfP6.js → index-BqltWojQ.js} +1 -1
  111. package/src/assets/web-panel/assets/{index-DAOpQvl_.js → index-CGiIyTZw.js} +1 -1
  112. package/src/assets/web-panel/assets/index-CKB0vbJU.js +1 -0
  113. package/src/assets/web-panel/assets/{index-C4DV_5uA.js → index-CZ2wUEgr.js} +1 -1
  114. package/src/assets/web-panel/assets/{index-yYTQZypF.js → index-CZpfzcuy.js} +1 -1
  115. package/src/assets/web-panel/assets/{index-et5JIPHO.js → index-Ckrk3pIV.js} +1 -1
  116. package/src/assets/web-panel/assets/{index-DaA__-q1.js → index-CqoCZ5vR.js} +1 -1
  117. package/src/assets/web-panel/assets/{index-By6u5IV1.js → index-CyWBir7N.js} +1 -1
  118. package/src/assets/web-panel/assets/{index-CKTfZyfU.js → index-CyyGTMKd.js} +1 -1
  119. package/src/assets/web-panel/assets/{index-B_vGU-Js.js → index-D0BSkxYQ.js} +1 -1
  120. package/src/assets/web-panel/assets/{index-Cao5DZy7.js → index-D5YVopyr.js} +1 -1
  121. package/src/assets/web-panel/assets/{index-C3TSohzc.js → index-DKUBj36_.js} +1 -1
  122. package/src/assets/web-panel/assets/{index-BFIFJ5Dr.js → index-DM724NgU.js} +1 -1
  123. package/src/assets/web-panel/assets/{index-ConjaJro.js → index-DQRsQen0.js} +1 -1
  124. package/src/assets/web-panel/assets/index-DgoXmwqB.js +1 -0
  125. package/src/assets/web-panel/assets/{index-C9_48OzU.js → index-DikJ0Qqe.js} +1 -1
  126. package/src/assets/web-panel/assets/{index-C0k_Nqg9.js → index-Dn2dSdXx.js} +1 -1
  127. package/src/assets/web-panel/assets/{index-DE-hmI2l.js → index-DyUaFWEk.js} +1 -1
  128. package/src/assets/web-panel/assets/{index-so8eIIxF.js → index-Hfe_dEGx.js} +1 -1
  129. package/src/assets/web-panel/assets/{index-FcV8J_Qz.js → index-_JjRLSD0.js} +1 -1
  130. package/src/assets/web-panel/assets/{index-D9LyiN-L.js → index-frSojpHj.js} +1 -1
  131. package/src/assets/web-panel/assets/{index-CX78k-qf.js → index-jK3ax6Z6.js} +1 -1
  132. package/src/assets/web-panel/assets/{index-C8UltbXK.js → index-qMuZ-NRR.js} +1 -1
  133. package/src/assets/web-panel/assets/{index-CqcLo5WV.js → index-q_fQ94SE.js} +1 -1
  134. package/src/assets/web-panel/assets/{initDefaultProps-CPYEIdpV.js → initDefaultProps-D4VdhJ9_.js} +1 -1
  135. package/src/assets/web-panel/assets/{motion-Cjim9pxs.js → motion-CZJA0MQW.js} +1 -1
  136. package/src/assets/web-panel/assets/{move-DuU-cgz3.js → move-BqEs7Sb1.js} +1 -1
  137. package/src/assets/web-panel/assets/{mtc-parser-DJHNAkta.js → mtc-parser-BpHEtsu1.js} +1 -1
  138. package/src/assets/web-panel/assets/{omit-DSQBgdBj.js → omit-BW94tY9W.js} +1 -1
  139. package/src/assets/web-panel/assets/{pickAttrs-CTmT_j5m.js → pickAttrs-BnBjPWP9.js} +1 -1
  140. package/src/assets/web-panel/assets/{placementArrow-Lfm6v-r4.js → placementArrow-uUkjQ_hu.js} +1 -1
  141. package/src/assets/web-panel/assets/{responsiveObserve-CgrcPcDi.js → responsiveObserve-B-klUqXK.js} +1 -1
  142. package/src/assets/web-panel/assets/{slide-CuE3110i.js → slide-CvY1cNAs.js} +1 -1
  143. package/src/assets/web-panel/assets/{statusUtils-DiK5nbpf.js → statusUtils-Ix5sEDT2.js} +1 -1
  144. package/src/assets/web-panel/assets/{styleChecker-DPEJdQaZ.js → styleChecker-C8qcXOne.js} +1 -1
  145. package/src/assets/web-panel/assets/{useFlexGapSupport-CnnodGmp.js → useFlexGapSupport-KNF6Kj8f.js} +1 -1
  146. package/src/assets/web-panel/assets/{useFs-DWF84Yav.js → useFs-DGJr345n.js} +1 -1
  147. package/src/assets/web-panel/assets/{usePersonalDataHub-a5b5Dw2f.js → usePersonalDataHub-BboN99rI.js} +1 -1
  148. package/src/assets/web-panel/assets/{vnode-DAluzXOq.js → vnode-Bgt8Vgte.js} +1 -1
  149. package/src/assets/web-panel/assets/{zoom-Dokg7uAZ.js → zoom-BhJtErgt.js} +1 -1
  150. package/src/assets/web-panel/index.html +1 -1
  151. package/src/command-help-index.json +1 -1
  152. package/src/commands/artifacts.js +98 -0
  153. package/src/commands/mcp.js +1 -1
  154. package/src/commands/review.js +221 -22
  155. package/src/commands/session-mcp-recovery.js +5 -5
  156. package/src/data/changelog.json +14 -1
  157. package/src/gateways/ws/ws-agent-handler.js +492 -14
  158. package/src/harness/jsonl-session-store.js +39 -1
  159. package/src/harness/mcp-client.js +172 -29
  160. package/src/harness/prompt-compressor.js +24 -0
  161. package/src/harness/provider-backed-compaction.js +205 -0
  162. package/src/lib/app-preview.js +3 -0
  163. package/src/lib/command-lifecycle-report.js +1340 -145
  164. package/src/lib/delivery-coordinator.js +30 -5
  165. package/src/lib/delivery-production-adapter.js +1777 -0
  166. package/src/lib/delivery-production-runner.js +240 -0
  167. package/src/lib/durable-security-store.js +29 -17
  168. package/src/lib/execution-trace/trace-context.cjs +179 -0
  169. package/src/lib/execution-trace/trace-context.js +8 -175
  170. package/src/lib/mcp-call-ledger-store.js +58 -6
  171. package/src/lib/mcp-host-recovery-runtime.js +2 -0
  172. package/src/lib/mcp-ledger-recovery-admission.js +39 -3
  173. package/src/lib/mcp-recovery-adjudication.js +256 -19
  174. package/src/lib/mcp-sandbox-policy.js +29 -0
  175. package/src/lib/mcp-stdio-capsule-builder-worker.cjs +495 -0
  176. package/src/lib/mcp-stdio-executable-identity.js +33 -2
  177. package/src/lib/mcp-stdio-immutable-vfs-resolver.cjs +830 -0
  178. package/src/lib/mcp-stdio-package-materialization.js +721 -333
  179. package/src/lib/observability/index.js +2 -0
  180. package/src/lib/observability/otlp-exporter.js +2 -2
  181. package/src/lib/packer/pack-update-applier.js +8 -1
  182. package/src/lib/packer/pkg-config-generator.js +34 -0
  183. package/src/lib/process-execution-broker/index.js +34 -5
  184. package/src/lib/process-execution-broker/platform-sandbox.js +82 -1
  185. package/src/lib/scheduler-kernel/contract.js +315 -0
  186. package/src/lib/scheduler-kernel/store.js +1345 -0
  187. package/src/lib/session-host-lease.js +403 -30
  188. package/src/lib/session-persistence-failure.js +4 -1
  189. package/src/lib/sub-agent-context.js +8 -0
  190. package/src/repl/agent-repl.js +285 -38
  191. package/src/runtime/agent-core.js +1044 -90
  192. package/src/runtime/headless-runner.js +260 -166
  193. package/src/runtime/headless-stream.js +254 -25
  194. package/src/assets/web-panel/assets/MarkdownRenderer-D53pHiHy.js +0 -1
  195. package/src/assets/web-panel/assets/Providers-CxC7JuYv.js +0 -1
  196. package/src/assets/web-panel/assets/devWarning-kB0nCNwN.js +0 -1
  197. package/src/assets/web-panel/assets/index-Bya64sZE.js +0 -1
  198. package/src/assets/web-panel/assets/index-D_FWpDZM.js +0 -1
@@ -186,6 +186,7 @@ export class ObservabilityRuntime {
186
186
  unit: "{invocation}",
187
187
  value: 1,
188
188
  monotonic: true,
189
+ aggregationTemporality: 1,
189
190
  attributes,
190
191
  },
191
192
  {
@@ -194,6 +195,7 @@ export class ObservabilityRuntime {
194
195
  unit: "ms",
195
196
  count: 1,
196
197
  sum: durationMs,
198
+ aggregationTemporality: 1,
197
199
  attributes,
198
200
  },
199
201
  ]);
@@ -924,7 +924,7 @@ export class OtlpExporter {
924
924
  description: metric.description || "",
925
925
  unit: metric.unit || "1",
926
926
  histogram: {
927
- aggregationTemporality: 2,
927
+ aggregationTemporality: metric.aggregationTemporality === 1 ? 1 : 2,
928
928
  dataPoints: [
929
929
  {
930
930
  ...point,
@@ -945,7 +945,7 @@ export class OtlpExporter {
945
945
  : { asDouble: Number(metric.value) || 0 };
946
946
  const data = { dataPoints: [{ ...point, ...number }] };
947
947
  if (metric.type === "sum" || metric.type === "counter") {
948
- data.aggregationTemporality = 2;
948
+ data.aggregationTemporality = metric.aggregationTemporality === 1 ? 1 : 2;
949
949
  data.isMonotonic = metric.monotonic !== false;
950
950
  return {
951
951
  name: metric.name,
@@ -46,6 +46,13 @@ export const _deps = {
46
46
  tmpdir: () => os.tmpdir(),
47
47
  };
48
48
 
49
+ // A real Windows sidecar performs path, lock-token and digest checks before it
50
+ // publishes the readiness marker. Loaded hosted runners have exceeded the old
51
+ // 10 second allowance while still completing successfully. Keep the parent in
52
+ // charge of the lock until the marker is verified, but give the safety
53
+ // preflight a bounded budget that also covers heavily contended machines.
54
+ export const WINDOWS_SIDECAR_READY_TIMEOUT_MS = 30_000;
55
+
49
56
  export async function scheduleReplace(ctx = {}) {
50
57
  const {
51
58
  newExePath,
@@ -778,7 +785,7 @@ function scheduleWindowsTransaction(ctx) {
778
785
  waitForReadyImpl({
779
786
  readyPath,
780
787
  transactionId,
781
- timeoutMs: 10_000,
788
+ timeoutMs: WINDOWS_SIDECAR_READY_TIMEOUT_MS,
782
789
  child,
783
790
  }),
784
791
  ) && !childError;
@@ -18,8 +18,41 @@
18
18
  */
19
19
 
20
20
  import fs from "node:fs";
21
+ import { createRequire } from "node:module";
21
22
  import path from "node:path";
22
23
 
24
+ function capsuleBuilderAssets(cliRoot) {
25
+ const requireFromCli = createRequire(path.join(cliRoot, "package.json"));
26
+ const realpath = fs.realpathSync.native || fs.realpathSync;
27
+ let packageJsonPath;
28
+ try {
29
+ packageJsonPath = realpath(
30
+ requireFromCli.resolve("esbuild-wasm/package.json"),
31
+ );
32
+ } catch (cause) {
33
+ throw new Error(
34
+ `Cannot resolve the pinned esbuild-wasm package for pkg assets: ${cause.message}`,
35
+ );
36
+ }
37
+ const packageRoot = path.dirname(packageJsonPath);
38
+ const sourceAssets = [
39
+ packageJsonPath,
40
+ path.join(packageRoot, "lib", "browser.js"),
41
+ path.join(packageRoot, "esbuild.wasm"),
42
+ path.join(cliRoot, "src", "lib", "mcp-stdio-capsule-builder-worker.cjs"),
43
+ path.join(cliRoot, "src", "lib", "mcp-stdio-immutable-vfs-resolver.cjs"),
44
+ ];
45
+ for (const asset of sourceAssets) {
46
+ const stat = fs.lstatSync(asset);
47
+ if (stat.isSymbolicLink() || !stat.isFile()) {
48
+ throw new Error(
49
+ `Pinned capsule builder pkg asset is not a regular file: ${asset}`,
50
+ );
51
+ }
52
+ }
53
+ return sourceAssets.map((asset) => realpath(asset));
54
+ }
55
+
23
56
  /**
24
57
  * @param {object} ctx
25
58
  * @param {string} ctx.cliRoot packages/cli/
@@ -396,6 +429,7 @@ export function generatePkgConfig(ctx) {
396
429
  `${posixify(cliRoot)}/src/lib/process-execution-broker/windows-sandbox-helper.exe`,
397
430
  `${posixify(cliRoot)}/src/lib/process-execution-broker/windows-sandbox.cs`,
398
431
  `${posixify(cliRoot)}/install/install.ps1`,
432
+ ...capsuleBuilderAssets(cliRoot).map(posixify),
399
433
  ];
400
434
  if (prebuildsDir) assets.push(`${posixify(prebuildsDir)}/**/*`);
401
435
  // Project mode: bundle the collected .chainlesschain/ snapshot as an asset.
@@ -73,7 +73,7 @@ const _ipcBus = null;
73
73
  function getTraceCtx() {
74
74
  if (!_traceCtx) {
75
75
  try {
76
- _traceCtx = require("../execution-trace/trace-context.js");
76
+ _traceCtx = require("../execution-trace/trace-context.cjs");
77
77
  } catch {
78
78
  _traceCtx = null;
79
79
  }
@@ -1218,6 +1218,7 @@ class ProcessExecutionBroker extends EventEmitter {
1218
1218
  "entrySnapshotAtomic",
1219
1219
  "runtimeLaunchAtomic",
1220
1220
  "mcpCapsuleCodeSnapshot",
1221
+ "runtimeDetachedChildSpawnVerified",
1221
1222
  ]) {
1222
1223
  if (
1223
1224
  plan.runtimeProbe[field] !== undefined &&
@@ -1237,6 +1238,7 @@ class ProcessExecutionBroker extends EventEmitter {
1237
1238
  "runtimeSnapshotBytes",
1238
1239
  "runtimeAttestedBytes",
1239
1240
  "entrySnapshotBytes",
1241
+ "capabilityCount",
1240
1242
  ]) {
1241
1243
  if (
1242
1244
  plan.runtimeProbe[field] !== undefined &&
@@ -1758,6 +1760,7 @@ class ProcessExecutionBroker extends EventEmitter {
1758
1760
  plan.runtimeProbe.runtimeLaunchAtomic === true &&
1759
1761
  plan.runtimeProbe.runtimeLaunchMechanism ===
1760
1762
  "bwrap-descriptor-mount-node-runtime-exec-v1" &&
1763
+ plan.runtimeProbe.runtimeDetachedChildSpawnVerified === true &&
1761
1764
  plan.runtimeProbe.supervisorDescriptorBound === true &&
1762
1765
  plan.runtimeProbe.pluginTreeContentSnapshot === true &&
1763
1766
  plan.runtimeProbe.runtimeLaunchPath ===
@@ -1794,6 +1797,7 @@ class ProcessExecutionBroker extends EventEmitter {
1794
1797
  (backend === "windows-appcontainer-job-restricted-token" &&
1795
1798
  plan.runtimeProbe.kind ===
1796
1799
  "windows-appcontainer-launch-attestation-v1" &&
1800
+ plan.runtimeProbe.capabilityCount === 0 &&
1797
1801
  policyAttested === true &&
1798
1802
  policyDigest !== null)) &&
1799
1803
  plan.runtimeProbe.contentSnapshotScope ===
@@ -1829,6 +1833,17 @@ class ProcessExecutionBroker extends EventEmitter {
1829
1833
  );
1830
1834
  }
1831
1835
  }
1836
+ if (
1837
+ plan.runtimeProbe.kind ===
1838
+ "windows-appcontainer-launch-attestation-v1" &&
1839
+ plan.runtimeProbe.runnable === true &&
1840
+ plan.runtimeProbe.capabilityCount !== 0
1841
+ ) {
1842
+ throw this._sandboxError(
1843
+ "invalid_sandbox_plan",
1844
+ "Windows AppContainer runtime evidence must attest zero capabilities",
1845
+ );
1846
+ }
1832
1847
  const genericWorkspaceEvidenceFields = [
1833
1848
  "contractDigest",
1834
1849
  "policyDigest",
@@ -2103,6 +2118,9 @@ class ProcessExecutionBroker extends EventEmitter {
2103
2118
  ...(plan.runtimeProbe.targetRuntime !== undefined
2104
2119
  ? { targetRuntime: plan.runtimeProbe.targetRuntime }
2105
2120
  : {}),
2121
+ ...(plan.runtimeProbe.capabilityCount !== undefined
2122
+ ? { capabilityCount: plan.runtimeProbe.capabilityCount }
2123
+ : {}),
2106
2124
  ...(plan.runtimeProbe.contentSnapshot !== undefined
2107
2125
  ? { contentSnapshot: plan.runtimeProbe.contentSnapshot }
2108
2126
  : {}),
@@ -2115,6 +2133,12 @@ class ProcessExecutionBroker extends EventEmitter {
2115
2133
  ...(plan.runtimeProbe.runtimeLaunchAtomic !== undefined
2116
2134
  ? { runtimeLaunchAtomic: plan.runtimeProbe.runtimeLaunchAtomic }
2117
2135
  : {}),
2136
+ ...(plan.runtimeProbe.runtimeDetachedChildSpawnVerified !== undefined
2137
+ ? {
2138
+ runtimeDetachedChildSpawnVerified:
2139
+ plan.runtimeProbe.runtimeDetachedChildSpawnVerified,
2140
+ }
2141
+ : {}),
2118
2142
  ...(plan.runtimeProbe.runtimeLaunchMechanism !== undefined
2119
2143
  ? {
2120
2144
  runtimeLaunchMechanism: plan.runtimeProbe.runtimeLaunchMechanism,
@@ -2922,10 +2946,15 @@ class ProcessExecutionBroker extends EventEmitter {
2922
2946
 
2923
2947
  _getTraceContext() {
2924
2948
  const traceCtx = getTraceCtx();
2925
- if (traceCtx && traceCtx.activeContext) {
2926
- return traceCtx.activeContext.value || null;
2927
- }
2928
- return null;
2949
+ const activeContext = traceCtx?.traceContext?.getCurrentContext?.() || null;
2950
+ if (!activeContext) return null;
2951
+ return {
2952
+ ...activeContext,
2953
+ traceparent: traceCtx.traceContext.formatTraceparent(
2954
+ activeContext.traceId,
2955
+ activeContext.spanId,
2956
+ ),
2957
+ };
2929
2958
  }
2930
2959
 
2931
2960
  _writeRplEntry(auditEntry, status = "started", error = null) {
@@ -159,6 +159,9 @@ const LINUX_BWRAP_PATH = "/usr/bin/bwrap";
159
159
  const LINUX_LDD_PATH = "/usr/bin/ldd";
160
160
  const LINUX_BWRAP_BACKEND = "linux-bwrap";
161
161
  const LINUX_BWRAP_NODE_PROBE_SENTINEL = "chainless-linux-bwrap-plugin-node-v1";
162
+ const LINUX_BWRAP_CHILD_RUNTIME_PROBE_SENTINEL =
163
+ "chainless-linux-bwrap-child-runtime-v1";
164
+ const LINUX_BWRAP_CHILD_RUNTIME_PROBE_FAILURE_STATUS = 86;
162
165
  const LINUX_BWRAP_MAX_PLUGIN_ENTRIES = 512;
163
166
  const LINUX_PLUGIN_TREE_SNAPSHOT_MAX_FILES = 256;
164
167
  const LINUX_PLUGIN_TREE_SNAPSHOT_MAX_BYTES = 256 * 1024 * 1024;
@@ -2544,6 +2547,7 @@ function waitForWindowsTargetIdentity(
2544
2547
  proc.sandboxAppContainerProfile =
2545
2548
  expectedAppContainer.appContainerProfileName;
2546
2549
  proc.sandboxAppContainerSid = expectedAppContainer.appContainerSid;
2550
+ proc.sandboxAppContainerCapabilityCount = identity.capabilityCount;
2547
2551
  }
2548
2552
  // ChildProcess.pid is a writable data property. The native ChildProcess
2549
2553
  // handle still points at the helper, so kill()/ref()/unref() retain Job
@@ -2747,6 +2751,7 @@ function appContainerReadinessResult(
2747
2751
  ...runtimeProbe,
2748
2752
  runnable: true,
2749
2753
  reason: null,
2754
+ capabilityCount: readiness.capabilityCount,
2750
2755
  ...(targetRuntime
2751
2756
  ? {
2752
2757
  probeRuntime: targetRuntime,
@@ -3877,6 +3882,7 @@ function linuxBubblewrapProbe(
3877
3882
  supervisorBinding = null,
3878
3883
  pluginTreeSnapshot = null,
3879
3884
  nativeDynamicClosure = null,
3885
+ runtimeDetachedChildSpawnVerified = false,
3880
3886
  ) {
3881
3887
  const native =
3882
3888
  typeof targetRuntime === "string" && targetRuntime.startsWith("native-");
@@ -3942,6 +3948,7 @@ function linuxBubblewrapProbe(
3942
3948
  reason,
3943
3949
  probeRuntime: "node",
3944
3950
  targetRuntime,
3951
+ runtimeDetachedChildSpawnVerified,
3945
3952
  contentSnapshot: snapshot,
3946
3953
  ...(snapshot
3947
3954
  ? {
@@ -6791,8 +6798,22 @@ function probeLinuxBubblewrapPolicy(
6791
6798
  ino: supervisorBinding.fileId.ino,
6792
6799
  bytes: supervisorBinding.bytes,
6793
6800
  });
6801
+ const childProbeSource = [
6802
+ 'const fs = require("node:fs");',
6803
+ "const sentinel = process.argv[1];",
6804
+ 'const raw = fs.readFileSync("/proc/self/stat", "utf8");',
6805
+ 'const close = raw.lastIndexOf(")");',
6806
+ 'const pid = Number(raw.slice(0, raw.indexOf(" ")));',
6807
+ "const fields = raw.slice(close + 2).trim().split(/\\s+/);",
6808
+ "const processGroupPid = Number(fields[2]);",
6809
+ "const sessionPid = Number(fields[3]);",
6810
+ "fs.writeSync(1, JSON.stringify({ sentinel, pid, processGroupPid, sessionPid }));",
6811
+ "if (processGroupPid !== pid || sessionPid !== pid) process.exit(2);",
6812
+ ].join("\n");
6794
6813
  const probeSource = [
6795
6814
  'const fs = require("node:fs");',
6815
+ 'const crypto = require("node:crypto");',
6816
+ 'const { spawnSync } = require("node:child_process");',
6796
6817
  `const expected = ${supervisorIdentity};`,
6797
6818
  'for (const name of fs.readdirSync("/proc/self/fd")) {',
6798
6819
  " let current;",
@@ -6809,10 +6830,62 @@ function probeLinuxBubblewrapPolicy(
6809
6830
  "} catch (error) {",
6810
6831
  ' if (error?.code !== "ENOENT") throw error;',
6811
6832
  "}",
6833
+ "// /tmp is a fresh private bwrap tmpfs; read the same O_EXCL inode by fd.",
6834
+ 'const childReportPath = "/tmp/.chainless-runtime-child-probe.json";',
6835
+ `const expectedChildSentinel = ${JSON.stringify(
6836
+ `${LINUX_BWRAP_CHILD_RUNTIME_PROBE_SENTINEL}:`,
6837
+ )} + crypto.randomBytes(16).toString("hex");`,
6838
+ "let reportFd;",
6839
+ "let runtimeDetachedChildSpawnVerified = false;",
6840
+ "try {",
6841
+ ' reportFd = fs.openSync(childReportPath, "wx+", 0o600);',
6842
+ ` const child = spawnSync("/opt/chainless/runtime/node", ["-e", ${JSON.stringify(
6843
+ childProbeSource,
6844
+ )}, expectedChildSentinel], {`,
6845
+ ' cwd: "/",',
6846
+ " detached: true,",
6847
+ " env: process.env,",
6848
+ " shell: false,",
6849
+ ' stdio: ["ignore", reportFd, "ignore"],',
6850
+ " timeout: 5_000,",
6851
+ " });",
6852
+ " const reportStat = fs.fstatSync(reportFd);",
6853
+ " const reportBytes = Number(reportStat.size);",
6854
+ " if (!child.error && child.status === 0 && child.signal === null &&",
6855
+ " Number.isSafeInteger(child.pid) && child.pid > 0 &&",
6856
+ " reportStat.isFile() && Number.isSafeInteger(reportBytes) &&",
6857
+ " reportBytes > 0 && reportBytes <= 1_024) {",
6858
+ " const reportBuffer = Buffer.alloc(reportBytes);",
6859
+ " const reportRead = fs.readSync(reportFd, reportBuffer, 0, reportBytes, 0);",
6860
+ " if (reportRead === reportBytes) {",
6861
+ ' const reportText = reportBuffer.toString("utf8");',
6862
+ " const report = JSON.parse(reportText);",
6863
+ " const canonicalReport = JSON.stringify({ sentinel: report.sentinel,",
6864
+ " pid: report.pid, processGroupPid: report.processGroupPid,",
6865
+ " sessionPid: report.sessionPid });",
6866
+ " runtimeDetachedChildSpawnVerified = reportText === canonicalReport &&",
6867
+ " report.sentinel === expectedChildSentinel &&",
6868
+ " Number.isSafeInteger(report.pid) && report.pid > 0 &&",
6869
+ " report.pid === child.pid && report.processGroupPid === report.pid &&",
6870
+ " report.sessionPid === report.pid;",
6871
+ " }",
6872
+ " }",
6873
+ "} catch {} finally {",
6874
+ " try { if (Number.isInteger(reportFd)) fs.closeSync(reportFd); } catch {",
6875
+ " runtimeDetachedChildSpawnVerified = false;",
6876
+ " }",
6877
+ " try { fs.unlinkSync(childReportPath); } catch {",
6878
+ " runtimeDetachedChildSpawnVerified = false;",
6879
+ " }",
6880
+ "}",
6881
+ `if (!runtimeDetachedChildSpawnVerified) process.exit(${LINUX_BWRAP_CHILD_RUNTIME_PROBE_FAILURE_STATUS});`,
6812
6882
  `process.stdout.write(${JSON.stringify(LINUX_BWRAP_NODE_PROBE_SENTINEL)});`,
6813
6883
  ].join("\n");
6814
6884
  let result;
6815
6885
  try {
6886
+ // These pipes connect the host Broker to bwrap and are created before
6887
+ // bwrap installs the target seccomp filter. The nested runtime probe above
6888
+ // inherits a regular-file descriptor and never requests UV_CREATE_PIPE.
6816
6889
  result = runtime.spawnSync(
6817
6890
  supervisorLaunch.command,
6818
6891
  [...policyArgs, "--", "/opt/chainless/runtime/node", "-e", probeSource],
@@ -6846,7 +6919,11 @@ function probeLinuxBubblewrapPolicy(
6846
6919
  return linuxBubblewrapProbe(
6847
6920
  true,
6848
6921
  false,
6849
- result?.error ? "probe_spawn_failed" : "probe_failed",
6922
+ result?.error
6923
+ ? "probe_spawn_failed"
6924
+ : result?.status === LINUX_BWRAP_CHILD_RUNTIME_PROBE_FAILURE_STATUS
6925
+ ? "detached_child_runtime_probe_failed"
6926
+ : "probe_failed",
6850
6927
  targetRuntime,
6851
6928
  contentSnapshot,
6852
6929
  supervisorBinding,
@@ -6869,6 +6946,9 @@ function probeLinuxBubblewrapPolicy(
6869
6946
  targetRuntime,
6870
6947
  contentSnapshot,
6871
6948
  supervisorBinding,
6949
+ null,
6950
+ null,
6951
+ true,
6872
6952
  );
6873
6953
  }
6874
6954
 
@@ -7588,6 +7668,7 @@ export function applyLinuxSandbox(
7588
7668
  supervisorBinding,
7589
7669
  pluginTreeSnapshot,
7590
7670
  nativeDynamicClosure,
7671
+ policyProbe.runtimeDetachedChildSpawnVerified === true,
7591
7672
  );
7592
7673
  const runtimeProbe = Object.freeze(
7593
7674
  capsuleContract
@@ -0,0 +1,315 @@
1
+ import { createHash } from "node:crypto";
2
+ import { Buffer } from "node:buffer";
3
+
4
+ export const SCHEDULER_SCHEMA_VERSION = 1;
5
+ export const AUTHORITY_ENVELOPE_VERSION = 1;
6
+
7
+ export const OCCURRENCE_STATUS = Object.freeze({
8
+ QUEUED: "queued",
9
+ RUNNING: "running",
10
+ RETRY_WAIT: "retry_wait",
11
+ SUCCEEDED: "succeeded",
12
+ DEAD_LETTER: "dead_letter",
13
+ });
14
+
15
+ export const DEFAULT_MAX_ATTEMPTS = 3;
16
+ export const MAX_JOB_ATTEMPTS = 32;
17
+ export const DEFAULT_HISTORY_LIMIT = 50;
18
+ export const MAX_HISTORY_LIMIT = 200;
19
+ export const MAX_JSON_BYTES = 1024 * 1024;
20
+
21
+ const AUTHORITY_FIELDS = new Set([
22
+ "schemaVersion",
23
+ "principal",
24
+ "tenantId",
25
+ "workspaceId",
26
+ "requestedCapabilities",
27
+ "authorizationRefs",
28
+ ]);
29
+ const PRINCIPAL_FIELDS = new Set(["type", "id"]);
30
+ const AUTHORIZATION_REF_FIELDS = new Set([
31
+ "decisionId",
32
+ "policyRevision",
33
+ "grantIds",
34
+ "approvalIds",
35
+ "delegationIds",
36
+ ]);
37
+
38
+ export class SchedulerKernelError extends Error {
39
+ constructor(code, message, details = undefined, options = undefined) {
40
+ super(message, options);
41
+ this.name = "SchedulerKernelError";
42
+ this.code = code;
43
+ if (details !== undefined) this.details = details;
44
+ }
45
+ }
46
+
47
+ export function invalidArgument(message, details = undefined) {
48
+ return new SchedulerKernelError(
49
+ "SCHEDULER_INVALID_ARGUMENT",
50
+ message,
51
+ details,
52
+ );
53
+ }
54
+
55
+ function assertPlainObject(value, field) {
56
+ if (value === null || typeof value !== "object" || Array.isArray(value)) {
57
+ throw invalidArgument(`${field} must be an object`);
58
+ }
59
+ const prototype = Object.getPrototypeOf(value);
60
+ if (prototype !== Object.prototype && prototype !== null) {
61
+ throw invalidArgument(`${field} must be a plain object`);
62
+ }
63
+ return value;
64
+ }
65
+
66
+ function assertKnownFields(value, fields, field) {
67
+ const unknown = Object.keys(value).filter((key) => !fields.has(key));
68
+ if (unknown.length > 0) {
69
+ throw invalidArgument(`${field} contains unknown fields`, {
70
+ fields: unknown.sort(),
71
+ });
72
+ }
73
+ }
74
+
75
+ export function normalizeIdentifier(value, field, { maxLength = 256 } = {}) {
76
+ if (typeof value !== "string") {
77
+ throw invalidArgument(`${field} must be a string`);
78
+ }
79
+ const normalized = value.trim();
80
+ const hasControlCharacter = [...normalized].some((character) => {
81
+ const codePoint = character.codePointAt(0);
82
+ return codePoint <= 31 || codePoint === 127;
83
+ });
84
+ if (
85
+ normalized.length === 0 ||
86
+ normalized.length > maxLength ||
87
+ hasControlCharacter
88
+ ) {
89
+ throw invalidArgument(`${field} is not a valid identifier`);
90
+ }
91
+ return normalized;
92
+ }
93
+
94
+ function normalizeOptionalIdentifier(value, field) {
95
+ if (value === undefined || value === null) return null;
96
+ return normalizeIdentifier(value, field);
97
+ }
98
+
99
+ function normalizeIdentifierList(value, field) {
100
+ if (value === undefined) return [];
101
+ if (!Array.isArray(value)) {
102
+ throw invalidArgument(`${field} must be an array`);
103
+ }
104
+ if (value.length > 256) {
105
+ throw invalidArgument(`${field} exceeds 256 entries`);
106
+ }
107
+ return [
108
+ ...new Set(
109
+ value.map((entry, index) =>
110
+ normalizeIdentifier(entry, `${field}[${index}]`),
111
+ ),
112
+ ),
113
+ ].sort();
114
+ }
115
+
116
+ /**
117
+ * Normalize the authority evidence carried by a job and every occurrence.
118
+ *
119
+ * This envelope is deliberately descriptive. The scheduler stores the actor,
120
+ * requested capabilities and immutable decision/grant references, but it does
121
+ * not infer that those references authorize execution. A future dispatcher
122
+ * must still resolve and enforce the referenced policy before doing work.
123
+ */
124
+ export function normalizeAuthorityEnvelope(input) {
125
+ const authority = assertPlainObject(input, "authority");
126
+ assertKnownFields(authority, AUTHORITY_FIELDS, "authority");
127
+ if (
128
+ authority.schemaVersion !== undefined &&
129
+ authority.schemaVersion !== AUTHORITY_ENVELOPE_VERSION
130
+ ) {
131
+ throw invalidArgument(
132
+ `authority.schemaVersion must be ${AUTHORITY_ENVELOPE_VERSION}`,
133
+ );
134
+ }
135
+
136
+ const principal = assertPlainObject(
137
+ authority.principal,
138
+ "authority.principal",
139
+ );
140
+ assertKnownFields(principal, PRINCIPAL_FIELDS, "authority.principal");
141
+ const refs = assertPlainObject(
142
+ authority.authorizationRefs ?? {},
143
+ "authority.authorizationRefs",
144
+ );
145
+ assertKnownFields(
146
+ refs,
147
+ AUTHORIZATION_REF_FIELDS,
148
+ "authority.authorizationRefs",
149
+ );
150
+
151
+ return {
152
+ schemaVersion: AUTHORITY_ENVELOPE_VERSION,
153
+ principal: {
154
+ type: normalizeIdentifier(principal.type, "authority.principal.type", {
155
+ maxLength: 64,
156
+ }),
157
+ id: normalizeIdentifier(principal.id, "authority.principal.id"),
158
+ },
159
+ tenantId: normalizeOptionalIdentifier(
160
+ authority.tenantId,
161
+ "authority.tenantId",
162
+ ),
163
+ workspaceId: normalizeOptionalIdentifier(
164
+ authority.workspaceId,
165
+ "authority.workspaceId",
166
+ ),
167
+ requestedCapabilities: normalizeIdentifierList(
168
+ authority.requestedCapabilities,
169
+ "authority.requestedCapabilities",
170
+ ),
171
+ authorizationRefs: {
172
+ decisionId: normalizeOptionalIdentifier(
173
+ refs.decisionId,
174
+ "authority.authorizationRefs.decisionId",
175
+ ),
176
+ policyRevision: normalizeOptionalIdentifier(
177
+ refs.policyRevision,
178
+ "authority.authorizationRefs.policyRevision",
179
+ ),
180
+ grantIds: normalizeIdentifierList(
181
+ refs.grantIds,
182
+ "authority.authorizationRefs.grantIds",
183
+ ),
184
+ approvalIds: normalizeIdentifierList(
185
+ refs.approvalIds,
186
+ "authority.authorizationRefs.approvalIds",
187
+ ),
188
+ delegationIds: normalizeIdentifierList(
189
+ refs.delegationIds,
190
+ "authority.authorizationRefs.delegationIds",
191
+ ),
192
+ },
193
+ };
194
+ }
195
+
196
+ function normalizeJsonValue(value, field, state, depth) {
197
+ if (depth > 64) throw invalidArgument(`${field} exceeds maximum JSON depth`);
198
+ if (
199
+ value === null ||
200
+ typeof value === "string" ||
201
+ typeof value === "boolean"
202
+ ) {
203
+ return value;
204
+ }
205
+ if (typeof value === "number") {
206
+ if (!Number.isFinite(value)) {
207
+ throw invalidArgument(`${field} contains a non-finite number`);
208
+ }
209
+ return Object.is(value, -0) ? 0 : value;
210
+ }
211
+ if (typeof value !== "object") {
212
+ throw invalidArgument(`${field} contains a non-JSON value`);
213
+ }
214
+ if (state.seen.has(value)) {
215
+ throw invalidArgument(`${field} contains a cycle`);
216
+ }
217
+ state.seen.add(value);
218
+ try {
219
+ if (Array.isArray(value)) {
220
+ return value.map((entry, index) =>
221
+ normalizeJsonValue(entry, `${field}[${index}]`, state, depth + 1),
222
+ );
223
+ }
224
+ assertPlainObject(value, field);
225
+ // A JSON object may legitimately contain an own `__proto__` key. A normal
226
+ // object assignment would interpret that key as a prototype mutation and
227
+ // silently drop it from the canonical bytes.
228
+ const normalized = Object.create(null);
229
+ for (const key of Object.keys(value).sort()) {
230
+ normalized[key] = normalizeJsonValue(
231
+ value[key],
232
+ `${field}.${key}`,
233
+ state,
234
+ depth + 1,
235
+ );
236
+ }
237
+ return normalized;
238
+ } finally {
239
+ state.seen.delete(value);
240
+ }
241
+ }
242
+
243
+ export function normalizeJson(value, field = "value") {
244
+ const normalized = normalizeJsonValue(value, field, { seen: new Set() }, 0);
245
+ const encoded = JSON.stringify(normalized);
246
+ if (Buffer.byteLength(encoded, "utf8") > MAX_JSON_BYTES) {
247
+ throw invalidArgument(`${field} exceeds ${MAX_JSON_BYTES} encoded bytes`);
248
+ }
249
+ return normalized;
250
+ }
251
+
252
+ export function canonicalJson(value, field = "value") {
253
+ return JSON.stringify(normalizeJson(value, field));
254
+ }
255
+
256
+ export function normalizeEpochMs(value, field) {
257
+ if (!Number.isSafeInteger(value) || value < 0) {
258
+ throw invalidArgument(
259
+ `${field} must be a non-negative safe-integer epoch ms`,
260
+ );
261
+ }
262
+ return value;
263
+ }
264
+
265
+ export function normalizeMaxAttempts(value = DEFAULT_MAX_ATTEMPTS) {
266
+ if (!Number.isSafeInteger(value) || value < 1 || value > MAX_JOB_ATTEMPTS) {
267
+ throw invalidArgument(
268
+ `maxAttempts must be an integer between 1 and ${MAX_JOB_ATTEMPTS}`,
269
+ );
270
+ }
271
+ return value;
272
+ }
273
+
274
+ export function normalizeHistoryLimit(value = DEFAULT_HISTORY_LIMIT) {
275
+ if (!Number.isSafeInteger(value) || value < 1) {
276
+ throw invalidArgument("history limit must be a positive integer");
277
+ }
278
+ return Math.min(value, MAX_HISTORY_LIMIT);
279
+ }
280
+
281
+ /**
282
+ * Stable identity for one logical firing. All adapters must supply the same
283
+ * triggerKey for the same logical schedule. Including jobRevision prevents an
284
+ * edited job from silently inheriting the old definition's idempotency slot.
285
+ */
286
+ export function deriveOccurrenceIdentity({
287
+ jobId,
288
+ jobRevision,
289
+ scheduledFor,
290
+ triggerKey,
291
+ }) {
292
+ const material = canonicalJson(
293
+ {
294
+ contractVersion: SCHEDULER_SCHEMA_VERSION,
295
+ jobId: normalizeIdentifier(jobId, "jobId"),
296
+ jobRevision:
297
+ Number.isSafeInteger(jobRevision) && jobRevision >= 1
298
+ ? jobRevision
299
+ : (() => {
300
+ throw invalidArgument("jobRevision must be a positive integer");
301
+ })(),
302
+ scheduledFor: normalizeEpochMs(scheduledFor, "scheduledFor"),
303
+ triggerKey: normalizeIdentifier(triggerKey, "triggerKey"),
304
+ },
305
+ "occurrenceIdentity",
306
+ );
307
+ const digest = createHash("sha256")
308
+ .update("chainlesschain.scheduler.occurrence.v1\0", "utf8")
309
+ .update(material, "utf8")
310
+ .digest("hex");
311
+ return Object.freeze({
312
+ occurrenceId: `occ_${digest}`,
313
+ idempotencyKey: `scheduler:v1:${digest}`,
314
+ });
315
+ }