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.
- package/package.json +3 -2
- package/scripts/command-lifecycle-report.mjs +330 -38
- package/src/assets/web-panel/assets/{AIOps-DIY6KxV-.js → AIOps-DB5X7qqV.js} +1 -1
- package/src/assets/web-panel/assets/{ActionButton-BXH0muPt.js → ActionButton-DxHRtKLk.js} +1 -1
- package/src/assets/web-panel/assets/{Analytics-D39w8vmg.js → Analytics-DIH5QrxT.js} +2 -2
- package/src/assets/web-panel/assets/{AppLayout-CUU1zvLs.js → AppLayout-uu0oxVz7.js} +4 -4
- package/src/assets/web-panel/assets/{Artifacts-D8azHO3T.js → Artifacts-BNSMiW9Y.js} +1 -1
- package/src/assets/web-panel/assets/{Audit-D0tAwXTg.js → Audit-DcAzB4_x.js} +1 -1
- package/src/assets/web-panel/assets/{BackgroundAgents-C29mhJll.js → BackgroundAgents-ClSsvjIc.js} +1 -1
- package/src/assets/web-panel/assets/{Backup-D6Aa1Gro.js → Backup-BMciY0js.js} +1 -1
- package/src/assets/web-panel/assets/{BaseInput-B1uoHbPQ.js → BaseInput-Hn3b57Sh.js} +1 -1
- package/src/assets/web-panel/assets/{Chat-Bs4DLOR5.js → Chat-CoSlC53V.js} +5 -5
- package/src/assets/web-panel/assets/{ChatBubbleRenderer-DUYIBSle.js → ChatBubbleRenderer-CFevJ3So.js} +1 -1
- package/src/assets/web-panel/assets/{Checkbox-CNcBPEa4.js → Checkbox-B4L4Eu4w.js} +1 -1
- package/src/assets/web-panel/assets/{Codegen-CIY89763.js → Codegen-C77FgRxF.js} +1 -1
- package/src/assets/web-panel/assets/{Col-CsPYFmgo.js → Col-BrLtJLH3.js} +1 -1
- package/src/assets/web-panel/assets/{Community-BBocEge4.js → Community-Be7pXXhA.js} +1 -1
- package/src/assets/web-panel/assets/{Compact-TQ8Ux0Lf.js → Compact-DuGPecjo.js} +1 -1
- package/src/assets/web-panel/assets/{Compliance-DfBsZTft.js → Compliance-CzS66mjP.js} +1 -1
- package/src/assets/web-panel/assets/{Cowork-Cg-I6FVH.js → Cowork-CEhjCoyK.js} +2 -2
- package/src/assets/web-panel/assets/{Cron-DTQzfRTp.js → Cron-D7ir8Vja.js} +2 -2
- package/src/assets/web-panel/assets/{Crosschain-DGxEKGpR.js → Crosschain-BcXmuLxf.js} +1 -1
- package/src/assets/web-panel/assets/{DID-DLZ1RDSr.js → DID-Cto1dtBy.js} +2 -2
- package/src/assets/web-panel/assets/{Dashboard-XneICKNI.js → Dashboard-B7KjgwOX.js} +2 -2
- package/src/assets/web-panel/assets/{Dropdown-BRU00J1-.js → Dropdown-COMqxijg.js} +1 -1
- package/src/assets/web-panel/assets/{EmailListRenderer-BzCbJuAy.js → EmailListRenderer-BZrya-nf.js} +1 -1
- package/src/assets/web-panel/assets/{FamilyGuardDashboard-D7BRyNBG.js → FamilyGuardDashboard-AmVeMq0J.js} +1 -1
- package/src/assets/web-panel/assets/{Federation-D9DeFIhW.js → Federation-kZU8c-27.js} +1 -1
- package/src/assets/web-panel/assets/{FormItemContext-6rAtA9Ko.js → FormItemContext-sOV22v2-.js} +1 -1
- package/src/assets/web-panel/assets/{GenericCardRenderer-8Hqy80pU.js → GenericCardRenderer-BUuLtGCz.js} +1 -1
- package/src/assets/web-panel/assets/{Git-Cmvkj1rt.js → Git-K-ZxsLsH.js} +2 -2
- package/src/assets/web-panel/assets/{Governance-BRiQUjjw.js → Governance-D3OEnawI.js} +1 -1
- package/src/assets/web-panel/assets/{Inference-CKN_EzVf.js → Inference-Bll0gRmP.js} +1 -1
- package/src/assets/web-panel/assets/{KnowledgeGraph-BCDHwpwQ.js → KnowledgeGraph-DBXwjr53.js} +1 -1
- package/src/assets/web-panel/assets/{Logs-DK9ovhaB.js → Logs-CEzPwEfP.js} +2 -2
- package/src/assets/web-panel/assets/MarkdownRenderer-CLmCbHx6.js +1 -0
- package/src/assets/web-panel/assets/{Marketplace-BILPGakp.js → Marketplace-BYyRyOPz.js} +1 -1
- package/src/assets/web-panel/assets/{McpTools-DTVCOr9-.js → McpTools-DVu8w_rM.js} +3 -3
- package/src/assets/web-panel/assets/{Memory-Hzc1iow4.js → Memory-tenuZ_hO.js} +2 -2
- package/src/assets/web-panel/assets/{MobileBridge-GXfgnGYV.js → MobileBridge-Bxgy4ozS.js} +1 -1
- package/src/assets/web-panel/assets/{MobileProjects-B-3TJWsS.js → MobileProjects-Dgx1D6sc.js} +1 -1
- package/src/assets/web-panel/assets/{Mtc-DwSJMWtK.js → Mtc-CRfJ8r8j.js} +4 -4
- package/src/assets/web-panel/assets/{MtcAudit-D2uxf-DA.js → MtcAudit-BBJq4Aiz.js} +4 -4
- package/src/assets/web-panel/assets/{Multisig-DG6kbKK0.js → Multisig-B3ii9lai.js} +3 -3
- package/src/assets/web-panel/assets/{NLProgramming-f5p49EDU.js → NLProgramming-FIi-Cubv.js} +1 -1
- package/src/assets/web-panel/assets/{Notes-FUgHyzSD.js → Notes-DIq8MPf1.js} +3 -3
- package/src/assets/web-panel/assets/{NotificationSettings-DCHGee7e.js → NotificationSettings-BFOD9eGd.js} +1 -1
- package/src/assets/web-panel/assets/{OrderTableRenderer-Dm6LlIoa.js → OrderTableRenderer--41u7vre.js} +1 -1
- package/src/assets/web-panel/assets/{Organization-BawA7qAg.js → Organization-B_0AWztW.js} +4 -4
- package/src/assets/web-panel/assets/{Overflow-D6a5wZU1.js → Overflow-B6I7TuXv.js} +1 -1
- package/src/assets/web-panel/assets/{P2P-K8WavwnA.js → P2P-Cdf6qens.js} +2 -2
- package/src/assets/web-panel/assets/{PdhVaultBrowser-4JAJYaqx.js → PdhVaultBrowser-B_0FcQJY.js} +3 -3
- package/src/assets/web-panel/assets/{Permissions-BDWvi8PD.js → Permissions-6nFBjsQp.js} +3 -3
- package/src/assets/web-panel/assets/{PersonalDataHub-DF_K4E9i.js → PersonalDataHub-nQYV7xpn.js} +3 -3
- package/src/assets/web-panel/assets/{Pipeline-BYeZlesI.js → Pipeline-CBVod5jm.js} +1 -1
- package/src/assets/web-panel/assets/{Privacy-BIHlgnIc.js → Privacy-D52jW1Nt.js} +1 -1
- package/src/assets/web-panel/assets/{ProjectInit-Cvf-6B5P.js → ProjectInit-Bdx3BYhf.js} +2 -2
- package/src/assets/web-panel/assets/{ProjectSettings-CnwfJrRz.js → ProjectSettings-b7xr4DUa.js} +2 -2
- package/src/assets/web-panel/assets/{Projects-b-jPXzF4.js → Projects-DhfifCKx.js} +1 -1
- package/src/assets/web-panel/assets/Providers-Dy_PgDMr.js +1 -0
- package/src/assets/web-panel/assets/{QrScannerModal-VLf3gAfL.js → QrScannerModal-FQKSmc3e.js} +1 -1
- package/src/assets/web-panel/assets/{QuickAsk-B5vGjRiF.js → QuickAsk-CSTrhzcF.js} +1 -1
- package/src/assets/web-panel/assets/{Recommend--UW4iYs-.js → Recommend-CxMABOUh.js} +1 -1
- package/src/assets/web-panel/assets/{RemoteSession-DMSPcfKR.js → RemoteSession-Cv2T8yRa.js} +2 -2
- package/src/assets/web-panel/assets/{Reputation-kQXlEHmj.js → Reputation-CE1k4Vvu.js} +1 -1
- package/src/assets/web-panel/assets/{Row-CxGu3pit.js → Row-BNgNcAV_.js} +1 -1
- package/src/assets/web-panel/assets/{RssFeed-Bi5lo8MZ.js → RssFeed-5ZiBZ3ph.js} +3 -3
- package/src/assets/web-panel/assets/{Search-Cq_H5RTh.js → Search-mDOIF1UV.js} +1 -1
- package/src/assets/web-panel/assets/{Security-YFbmkaQS.js → Security-CtuCTC-e.js} +3 -3
- package/src/assets/web-panel/assets/{Services-DLOGdaLm.js → Services-DxFO2Zg7.js} +2 -2
- package/src/assets/web-panel/assets/{Skeleton-CteIxb3D.js → Skeleton-CKmKkH9y.js} +1 -1
- package/src/assets/web-panel/assets/{Skills-CccopaIZ.js → Skills-Bmzl9qkk.js} +1 -1
- package/src/assets/web-panel/assets/{Sla-AzfEQKX1.js → Sla-9RqaWHOC.js} +1 -1
- package/src/assets/web-panel/assets/{SpeechSettings-ekGo5l77.js → SpeechSettings-CGdzFIYw.js} +1 -1
- package/src/assets/web-panel/assets/{SyncSettings-BmZmAgYH.js → SyncSettings-CvNuCeBT.js} +2 -2
- package/src/assets/web-panel/assets/{Tasks-D7q3Z4uL.js → Tasks-B_sLdcnw.js} +1 -1
- package/src/assets/web-panel/assets/{Templates-CX1Tgeka.js → Templates-D9_YijdK.js} +1 -1
- package/src/assets/web-panel/assets/{Tenant-Zc59jF4i.js → Tenant-Cuc7BUSP.js} +1 -1
- package/src/assets/web-panel/assets/{Terminal-CXOD7y83.js → Terminal-6VYRnJgd.js} +2 -2
- package/src/assets/web-panel/assets/{TimelineRenderer-B1aBHPBo.js → TimelineRenderer-GtK8xd8Y.js} +1 -1
- package/src/assets/web-panel/assets/{Tokens-zALlOq_U.js → Tokens-BZ94Lt5W.js} +1 -1
- package/src/assets/web-panel/assets/{Trigger-CQfJMXaq.js → Trigger-DEcZgDaX.js} +1 -1
- package/src/assets/web-panel/assets/{Trust-BU0RcM-w.js → Trust-CC3z9THe.js} +1 -1
- package/src/assets/web-panel/assets/{UkeySign-IFZ3HsbH.js → UkeySign-Cvtw8cV3.js} +1 -1
- package/src/assets/web-panel/assets/{VideoEditing-BzslgW0v.js → VideoEditing-CL4e6Bn3.js} +1 -1
- package/src/assets/web-panel/assets/{Wallet-BZAcQNS6.js → Wallet-ycEemP27.js} +2 -2
- package/src/assets/web-panel/assets/{WebAuthn-CBK1DMi0.js → WebAuthn-DgZ4X_aw.js} +5 -5
- package/src/assets/web-panel/assets/{WorkflowEditor-BlMTPzO-.js → WorkflowEditor-D0qMhSv7.js} +1 -1
- package/src/assets/web-panel/assets/{chat-B51-8QVn.js → chat-DsAg01Zn.js} +1 -1
- package/src/assets/web-panel/assets/{colors-504RTByU.js → colors-B0oqAhrQ.js} +1 -1
- package/src/assets/web-panel/assets/{compact-item-C-GP6IQM.js → compact-item-CmX0Rabz.js} +1 -1
- package/src/assets/web-panel/assets/{createContext-DOkIj7ID.js → createContext-Be4cn1Ub.js} +1 -1
- package/src/assets/web-panel/assets/devWarning-CP2qzG6D.js +1 -0
- package/src/assets/web-panel/assets/{hasIn-DN7V-_FW.js → hasIn-D5v12kVq.js} +1 -1
- package/src/assets/web-panel/assets/{index-kt_5rfcZ.js → index-0eWLkQKT.js} +1 -1
- package/src/assets/web-panel/assets/{index-CUhQpiYv.js → index-301ogenv.js} +1 -1
- package/src/assets/web-panel/assets/{index-f4Bo4KXm.js → index-5LGY0JbG.js} +1 -1
- package/src/assets/web-panel/assets/{index--n-ZciV3.js → index-7VKDn-t1.js} +1 -1
- package/src/assets/web-panel/assets/{index-D1phyca8.js → index-8WvYGnON.js} +1 -1
- package/src/assets/web-panel/assets/{index-7jIqHbzw.js → index-B1YGjOWC.js} +1 -1
- package/src/assets/web-panel/assets/{index-DEMPUVa3.js → index-B3mUHuVc.js} +1 -1
- package/src/assets/web-panel/assets/{index-Bkxhaeta.js → index-BA4oulaP.js} +1 -1
- package/src/assets/web-panel/assets/{index-CYopNjcJ.js → index-BBREnn1V.js} +1 -1
- package/src/assets/web-panel/assets/{index-P2mfY27Z.js → index-BBvyHguy.js} +1 -1
- package/src/assets/web-panel/assets/{index-DA2f1UV3.js → index-BKmsS2jj.js} +1 -1
- package/src/assets/web-panel/assets/{index-CP2A226L.js → index-BRIqLy1m.js} +1 -1
- package/src/assets/web-panel/assets/{index-CfiEM64n.js → index-BXGSbelg.js} +3 -3
- package/src/assets/web-panel/assets/{index-B2-x9bS8.js → index-BaE0Ui07.js} +1 -1
- package/src/assets/web-panel/assets/{index-CySTJKeq.js → index-BoAIAZ5C.js} +1 -1
- package/src/assets/web-panel/assets/{index-kAqQXfP6.js → index-BqltWojQ.js} +1 -1
- package/src/assets/web-panel/assets/{index-DAOpQvl_.js → index-CGiIyTZw.js} +1 -1
- package/src/assets/web-panel/assets/index-CKB0vbJU.js +1 -0
- package/src/assets/web-panel/assets/{index-C4DV_5uA.js → index-CZ2wUEgr.js} +1 -1
- package/src/assets/web-panel/assets/{index-yYTQZypF.js → index-CZpfzcuy.js} +1 -1
- package/src/assets/web-panel/assets/{index-et5JIPHO.js → index-Ckrk3pIV.js} +1 -1
- package/src/assets/web-panel/assets/{index-DaA__-q1.js → index-CqoCZ5vR.js} +1 -1
- package/src/assets/web-panel/assets/{index-By6u5IV1.js → index-CyWBir7N.js} +1 -1
- package/src/assets/web-panel/assets/{index-CKTfZyfU.js → index-CyyGTMKd.js} +1 -1
- package/src/assets/web-panel/assets/{index-B_vGU-Js.js → index-D0BSkxYQ.js} +1 -1
- package/src/assets/web-panel/assets/{index-Cao5DZy7.js → index-D5YVopyr.js} +1 -1
- package/src/assets/web-panel/assets/{index-C3TSohzc.js → index-DKUBj36_.js} +1 -1
- package/src/assets/web-panel/assets/{index-BFIFJ5Dr.js → index-DM724NgU.js} +1 -1
- package/src/assets/web-panel/assets/{index-ConjaJro.js → index-DQRsQen0.js} +1 -1
- package/src/assets/web-panel/assets/index-DgoXmwqB.js +1 -0
- package/src/assets/web-panel/assets/{index-C9_48OzU.js → index-DikJ0Qqe.js} +1 -1
- package/src/assets/web-panel/assets/{index-C0k_Nqg9.js → index-Dn2dSdXx.js} +1 -1
- package/src/assets/web-panel/assets/{index-DE-hmI2l.js → index-DyUaFWEk.js} +1 -1
- package/src/assets/web-panel/assets/{index-so8eIIxF.js → index-Hfe_dEGx.js} +1 -1
- package/src/assets/web-panel/assets/{index-FcV8J_Qz.js → index-_JjRLSD0.js} +1 -1
- package/src/assets/web-panel/assets/{index-D9LyiN-L.js → index-frSojpHj.js} +1 -1
- package/src/assets/web-panel/assets/{index-CX78k-qf.js → index-jK3ax6Z6.js} +1 -1
- package/src/assets/web-panel/assets/{index-C8UltbXK.js → index-qMuZ-NRR.js} +1 -1
- package/src/assets/web-panel/assets/{index-CqcLo5WV.js → index-q_fQ94SE.js} +1 -1
- package/src/assets/web-panel/assets/{initDefaultProps-CPYEIdpV.js → initDefaultProps-D4VdhJ9_.js} +1 -1
- package/src/assets/web-panel/assets/{motion-Cjim9pxs.js → motion-CZJA0MQW.js} +1 -1
- package/src/assets/web-panel/assets/{move-DuU-cgz3.js → move-BqEs7Sb1.js} +1 -1
- package/src/assets/web-panel/assets/{mtc-parser-DJHNAkta.js → mtc-parser-BpHEtsu1.js} +1 -1
- package/src/assets/web-panel/assets/{omit-DSQBgdBj.js → omit-BW94tY9W.js} +1 -1
- package/src/assets/web-panel/assets/{pickAttrs-CTmT_j5m.js → pickAttrs-BnBjPWP9.js} +1 -1
- package/src/assets/web-panel/assets/{placementArrow-Lfm6v-r4.js → placementArrow-uUkjQ_hu.js} +1 -1
- package/src/assets/web-panel/assets/{responsiveObserve-CgrcPcDi.js → responsiveObserve-B-klUqXK.js} +1 -1
- package/src/assets/web-panel/assets/{slide-CuE3110i.js → slide-CvY1cNAs.js} +1 -1
- package/src/assets/web-panel/assets/{statusUtils-DiK5nbpf.js → statusUtils-Ix5sEDT2.js} +1 -1
- package/src/assets/web-panel/assets/{styleChecker-DPEJdQaZ.js → styleChecker-C8qcXOne.js} +1 -1
- package/src/assets/web-panel/assets/{useFlexGapSupport-CnnodGmp.js → useFlexGapSupport-KNF6Kj8f.js} +1 -1
- package/src/assets/web-panel/assets/{useFs-DWF84Yav.js → useFs-DGJr345n.js} +1 -1
- package/src/assets/web-panel/assets/{usePersonalDataHub-a5b5Dw2f.js → usePersonalDataHub-BboN99rI.js} +1 -1
- package/src/assets/web-panel/assets/{vnode-DAluzXOq.js → vnode-Bgt8Vgte.js} +1 -1
- package/src/assets/web-panel/assets/{zoom-Dokg7uAZ.js → zoom-BhJtErgt.js} +1 -1
- package/src/assets/web-panel/index.html +1 -1
- package/src/command-help-index.json +1 -1
- package/src/commands/artifacts.js +98 -0
- package/src/commands/mcp.js +1 -1
- package/src/commands/review.js +221 -22
- package/src/commands/session-mcp-recovery.js +5 -5
- package/src/data/changelog.json +14 -1
- package/src/gateways/ws/ws-agent-handler.js +492 -14
- package/src/harness/jsonl-session-store.js +39 -1
- package/src/harness/mcp-client.js +172 -29
- package/src/harness/prompt-compressor.js +24 -0
- package/src/harness/provider-backed-compaction.js +205 -0
- package/src/lib/app-preview.js +3 -0
- package/src/lib/command-lifecycle-report.js +1340 -145
- package/src/lib/delivery-coordinator.js +30 -5
- package/src/lib/delivery-production-adapter.js +1777 -0
- package/src/lib/delivery-production-runner.js +240 -0
- package/src/lib/durable-security-store.js +29 -17
- package/src/lib/execution-trace/trace-context.cjs +179 -0
- package/src/lib/execution-trace/trace-context.js +8 -175
- package/src/lib/mcp-call-ledger-store.js +58 -6
- package/src/lib/mcp-host-recovery-runtime.js +2 -0
- package/src/lib/mcp-ledger-recovery-admission.js +39 -3
- package/src/lib/mcp-recovery-adjudication.js +256 -19
- package/src/lib/mcp-sandbox-policy.js +29 -0
- package/src/lib/mcp-stdio-capsule-builder-worker.cjs +495 -0
- package/src/lib/mcp-stdio-executable-identity.js +33 -2
- package/src/lib/mcp-stdio-immutable-vfs-resolver.cjs +830 -0
- package/src/lib/mcp-stdio-package-materialization.js +721 -333
- package/src/lib/observability/index.js +2 -0
- package/src/lib/observability/otlp-exporter.js +2 -2
- package/src/lib/packer/pack-update-applier.js +8 -1
- package/src/lib/packer/pkg-config-generator.js +34 -0
- package/src/lib/process-execution-broker/index.js +34 -5
- package/src/lib/process-execution-broker/platform-sandbox.js +82 -1
- package/src/lib/scheduler-kernel/contract.js +315 -0
- package/src/lib/scheduler-kernel/store.js +1345 -0
- package/src/lib/session-host-lease.js +403 -30
- package/src/lib/session-persistence-failure.js +4 -1
- package/src/lib/sub-agent-context.js +8 -0
- package/src/repl/agent-repl.js +285 -38
- package/src/runtime/agent-core.js +1044 -90
- package/src/runtime/headless-runner.js +260 -166
- package/src/runtime/headless-stream.js +254 -25
- package/src/assets/web-panel/assets/MarkdownRenderer-D53pHiHy.js +0 -1
- package/src/assets/web-panel/assets/Providers-CxC7JuYv.js +0 -1
- package/src/assets/web-panel/assets/devWarning-kB0nCNwN.js +0 -1
- package/src/assets/web-panel/assets/index-Bya64sZE.js +0 -1
- 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:
|
|
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.
|
|
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
|
-
|
|
2926
|
-
|
|
2927
|
-
|
|
2928
|
-
|
|
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
|
|
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
|
+
}
|