chainlesschain 0.165.4 → 0.165.6
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 +1 -1
- package/src/assets/web-panel/.build-hash +1 -1
- package/src/assets/web-panel/assets/{AIOps-C__nnraW.js → AIOps-vfoDb6HZ.js} +1 -1
- package/src/assets/web-panel/assets/{ActionButton-CsKWHEZ-.js → ActionButton-Cm5f7p93.js} +1 -1
- package/src/assets/web-panel/assets/{Analytics-BhIjfjWO.js → Analytics-yRuvgWxa.js} +3 -3
- package/src/assets/web-panel/assets/{AppLayout-BjuXFzWZ.js → AppLayout-BROsC4h1.js} +4 -4
- package/src/assets/web-panel/assets/{Artifacts-k6whDkZJ.js → Artifacts-CimHApb1.js} +1 -1
- package/src/assets/web-panel/assets/{Audit-C4FBMYRh.js → Audit-DI8kr8zI.js} +1 -1
- package/src/assets/web-panel/assets/{BackgroundAgents-C_vRrSXk.js → BackgroundAgents-DSLzKy-n.js} +1 -1
- package/src/assets/web-panel/assets/{Backup-CngFZeVp.js → Backup-DkH_n2r1.js} +1 -1
- package/src/assets/web-panel/assets/{BaseInput-B_H9EBRm.js → BaseInput-CKSrUoRA.js} +1 -1
- package/src/assets/web-panel/assets/{Chat-DLEC5kt7.js → Chat-ClwfMXUS.js} +3 -3
- package/src/assets/web-panel/assets/ChatBubbleRenderer-CjMaqfL7.js +1 -0
- package/src/assets/web-panel/assets/{Checkbox-m3ClR4xh.js → Checkbox-BYTehcQG.js} +1 -1
- package/src/assets/web-panel/assets/{Codegen-CmLSFrgp.js → Codegen-B8AMcp6J.js} +1 -1
- package/src/assets/web-panel/assets/{Col-DFGQteHN.js → Col-BNSogPo3.js} +1 -1
- package/src/assets/web-panel/assets/{Community-EUUVORHE.js → Community-JxPvGb8O.js} +1 -1
- package/src/assets/web-panel/assets/{Compact-2PSg0Yr7.js → Compact-DF7bFAgE.js} +1 -1
- package/src/assets/web-panel/assets/{Compliance-CcKur7gB.js → Compliance-Bvhqnwrq.js} +1 -1
- package/src/assets/web-panel/assets/{Cowork-DK_Fezfa.js → Cowork-BDS0CJvM.js} +3 -3
- package/src/assets/web-panel/assets/{Cron-Dbw89xl7.js → Cron-DqvRfxoQ.js} +2 -2
- package/src/assets/web-panel/assets/{Crosschain-CH66h_Ec.js → Crosschain-CDcIvnqS.js} +1 -1
- package/src/assets/web-panel/assets/{DID-D1igRb17.js → DID-D7jaUxU3.js} +2 -2
- package/src/assets/web-panel/assets/{Dashboard-DVAsrobk.js → Dashboard-B5A18wEV.js} +2 -2
- package/src/assets/web-panel/assets/{Dropdown-Dl7W8AQs.js → Dropdown-_6IVkGag.js} +1 -1
- package/src/assets/web-panel/assets/{EmailListRenderer-D5tpaEQC.js → EmailListRenderer-C9PeiKCG.js} +1 -1
- package/src/assets/web-panel/assets/{FamilyGuardDashboard-O_VQf8gq.js → FamilyGuardDashboard-DXjuEHrX.js} +1 -1
- package/src/assets/web-panel/assets/{Federation-BTVcgS9n.js → Federation-B4IAR9zv.js} +1 -1
- package/src/assets/web-panel/assets/{FormItemContext-BRnkJAIz.js → FormItemContext-CxQeSNx1.js} +1 -1
- package/src/assets/web-panel/assets/{GenericCardRenderer-8OWSCkDj.js → GenericCardRenderer-B8GS-Y3N.js} +1 -1
- package/src/assets/web-panel/assets/{Git-WXLYTQj2.js → Git-BS8SouUX.js} +2 -2
- package/src/assets/web-panel/assets/{Governance-BezaUhtc.js → Governance-Dq9aYb5Q.js} +1 -1
- package/src/assets/web-panel/assets/{Inference-CuRay3Gu.js → Inference-BrCEe9AJ.js} +1 -1
- package/src/assets/web-panel/assets/{KnowledgeGraph-DfWLDW1m.js → KnowledgeGraph-DybOpiY2.js} +1 -1
- package/src/assets/web-panel/assets/{Logs-C55y2pPY.js → Logs-DBpzbRfx.js} +2 -2
- package/src/assets/web-panel/assets/MarkdownRenderer-CFyt8iUd.js +1 -0
- package/src/assets/web-panel/assets/{Marketplace-Dk08Uycg.js → Marketplace-Cc7CCSCu.js} +1 -1
- package/src/assets/web-panel/assets/{McpTools-C11fSB5V.js → McpTools-DFaIhOfu.js} +3 -3
- package/src/assets/web-panel/assets/{Memory-mr0eZc97.js → Memory-KCqlp9ol.js} +2 -2
- package/src/assets/web-panel/assets/{MobileBridge-C0iCSRbb.js → MobileBridge-Bys84ySN.js} +1 -1
- package/src/assets/web-panel/assets/{MobileProjects-CY-xMG8Z.js → MobileProjects-B-6qU-je.js} +1 -1
- package/src/assets/web-panel/assets/{Mtc-Bplqc_Wc.js → Mtc-BgY-fvCq.js} +4 -4
- package/src/assets/web-panel/assets/{MtcAudit-BlzwsoSM.js → MtcAudit-B8pCmqQJ.js} +4 -4
- package/src/assets/web-panel/assets/{Multisig-BBLmTty6.js → Multisig-BbOmYutA.js} +3 -3
- package/src/assets/web-panel/assets/{NLProgramming-DtT1uf1W.js → NLProgramming-BocLQz_M.js} +1 -1
- package/src/assets/web-panel/assets/{Notes-DcqQAxTK.js → Notes-CSwbxEop.js} +3 -3
- package/src/assets/web-panel/assets/{NotificationSettings-cb0kWoZe.js → NotificationSettings-CqFNSYG3.js} +1 -1
- package/src/assets/web-panel/assets/{OrderTableRenderer-kw9HZQNN.js → OrderTableRenderer-C6gMfAk7.js} +1 -1
- package/src/assets/web-panel/assets/{Organization-DlTlbkUN.js → Organization-QrDXVdMJ.js} +4 -4
- package/src/assets/web-panel/assets/{Overflow-pp1qs-oY.js → Overflow-Cxntotjt.js} +1 -1
- package/src/assets/web-panel/assets/{P2P-BESLnyYz.js → P2P-CIdBOwNq.js} +2 -2
- package/src/assets/web-panel/assets/{PdhVaultBrowser-B-J1mGuf.js → PdhVaultBrowser-CT5aP8rD.js} +3 -3
- package/src/assets/web-panel/assets/{Permissions-B36PeT1_.js → Permissions-DH7OM8XU.js} +4 -4
- package/src/assets/web-panel/assets/{PersonalDataHub-CTK2ad-3.js → PersonalDataHub-CnJqbWce.js} +3 -3
- package/src/assets/web-panel/assets/{Pipeline-zJNGXsac.js → Pipeline-5C3HG1kH.js} +1 -1
- package/src/assets/web-panel/assets/{Privacy-b9fz0Qf0.js → Privacy-B4yIp7xK.js} +1 -1
- package/src/assets/web-panel/assets/{ProjectInit-DjZbZgiB.js → ProjectInit-BMvt-2M0.js} +2 -2
- package/src/assets/web-panel/assets/{ProjectSettings-C-hHJXlD.js → ProjectSettings-CvSTEMRG.js} +2 -2
- package/src/assets/web-panel/assets/Projects-BKBJxEek.js +1 -0
- package/src/assets/web-panel/assets/Providers-BdwUqFWN.js +1 -0
- package/src/assets/web-panel/assets/{QrScannerModal-A0yoQ3-3.js → QrScannerModal-DtnCKlTm.js} +1 -1
- package/src/assets/web-panel/assets/{QuickAsk-DrFgFJNC.js → QuickAsk-sTbgwyNr.js} +1 -1
- package/src/assets/web-panel/assets/{Recommend-KC8hF1g4.js → Recommend-Bzr14gSb.js} +1 -1
- package/src/assets/web-panel/assets/{RemoteSession-DGTPIk6u.js → RemoteSession-C_TF5IaJ.js} +2 -2
- package/src/assets/web-panel/assets/{Reputation-QmbwM7Yk.js → Reputation-GCAM4H2K.js} +1 -1
- package/src/assets/web-panel/assets/{Row-BxjJylia.js → Row-Dir0r6OQ.js} +1 -1
- package/src/assets/web-panel/assets/{RssFeed-B7Wda1J6.js → RssFeed-CpGnlecX.js} +2 -2
- package/src/assets/web-panel/assets/{Search-_plQ_YSA.js → Search-DMMamC6u.js} +1 -1
- package/src/assets/web-panel/assets/{Security-CD2YJZfx.js → Security-yqay5yG-.js} +4 -4
- package/src/assets/web-panel/assets/{Services-qQE7NmwE.js → Services-C5i2wh9G.js} +2 -2
- package/src/assets/web-panel/assets/{Skeleton-DgQqkFf7.js → Skeleton-vUqk35yj.js} +1 -1
- package/src/assets/web-panel/assets/{Skills-C4C0oFFt.js → Skills-DzcSkhot.js} +1 -1
- package/src/assets/web-panel/assets/{Sla-Px2N2B5c.js → Sla-CB6qbYNr.js} +1 -1
- package/src/assets/web-panel/assets/{SpeechSettings-DN6C3lQd.js → SpeechSettings-BHUI7cox.js} +1 -1
- package/src/assets/web-panel/assets/{SyncSettings-jm5aH_m7.js → SyncSettings-D-RrsxTJ.js} +2 -2
- package/src/assets/web-panel/assets/{Tasks-DlkJcv67.js → Tasks-DFxdOJiq.js} +1 -1
- package/src/assets/web-panel/assets/{Templates-DyrTHUBs.js → Templates-SKAob_Aq.js} +1 -1
- package/src/assets/web-panel/assets/{Tenant-mC434PtL.js → Tenant-BPfsfHQ-.js} +1 -1
- package/src/assets/web-panel/assets/{Terminal-o0_s3NgK.js → Terminal-8iw7FpUf.js} +2 -2
- package/src/assets/web-panel/assets/{TimelineRenderer-DAGlAXOw.js → TimelineRenderer-C4Gjj5Su.js} +1 -1
- package/src/assets/web-panel/assets/{Tokens-BnulS_bs.js → Tokens-COYhN18e.js} +1 -1
- package/src/assets/web-panel/assets/{Trigger-suQbVXYM.js → Trigger-B-JzXcf6.js} +1 -1
- package/src/assets/web-panel/assets/{Trust-DbIJAPo5.js → Trust-BJ5bg0nI.js} +1 -1
- package/src/assets/web-panel/assets/{UkeySign-CQNpbIHi.js → UkeySign-BG_ByRMf.js} +1 -1
- package/src/assets/web-panel/assets/{VideoEditing-D8aWCERk.js → VideoEditing-B3kOnSiU.js} +1 -1
- package/src/assets/web-panel/assets/{Wallet-CuLpYfZc.js → Wallet-CcoGFnK7.js} +2 -2
- package/src/assets/web-panel/assets/{WebAuthn-CsfG8ZMc.js → WebAuthn-DdT8vRC_.js} +4 -4
- package/src/assets/web-panel/assets/{WorkflowEditor-BYsEAZbd.js → WorkflowEditor-CZu3M9N8.js} +1 -1
- package/src/assets/web-panel/assets/{chat-FT4Um3-I.js → chat-DxJt3Qba.js} +1 -1
- package/src/assets/web-panel/assets/{colors-CKbS_Wlt.js → colors-DdYfwJaB.js} +1 -1
- package/src/assets/web-panel/assets/{compact-item-CFJtzuJM.js → compact-item-BG3SUzwj.js} +1 -1
- package/src/assets/web-panel/assets/{createContext-B9kSRW6K.js → createContext-By51z2-1.js} +1 -1
- package/src/assets/web-panel/assets/devWarning-DorRZcI9.js +1 -0
- package/src/assets/web-panel/assets/{hasIn-DR-x0Xpi.js → hasIn-B0apZ3KJ.js} +1 -1
- package/src/assets/web-panel/assets/{index-DjEP7WkH.js → index-3jWkIu5j.js} +1 -1
- package/src/assets/web-panel/assets/{index-7jHr_A_k.js → index-B3rWuPMW.js} +1 -1
- package/src/assets/web-panel/assets/{index-DcfxhKXR.js → index-B5rLiEo0.js} +1 -1
- package/src/assets/web-panel/assets/{index-tqSEHPRT.js → index-BBPf3TpM.js} +1 -1
- package/src/assets/web-panel/assets/index-BETiL0a3.js +1 -0
- package/src/assets/web-panel/assets/{index-Cr2FLUsD.js → index-BIrJfqO4.js} +1 -1
- package/src/assets/web-panel/assets/{index-Bt4fS2eL.js → index-BLIbGOLX.js} +1 -1
- package/src/assets/web-panel/assets/{index-BwR7yc2h.js → index-BP94CrBV.js} +1 -1
- package/src/assets/web-panel/assets/{index-BVPU43b1.js → index-BWF8_mkx.js} +1 -1
- package/src/assets/web-panel/assets/{index-DShSkww-.js → index-BYINoRLK.js} +1 -1
- package/src/assets/web-panel/assets/{index-Bzz0C1BZ.js → index-BZ_0qm4g.js} +1 -1
- package/src/assets/web-panel/assets/{index-KqDaCApw.js → index-BewKYrX9.js} +1 -1
- package/src/assets/web-panel/assets/{index-BW-t9n-q.js → index-BfWYsB9t.js} +1 -1
- package/src/assets/web-panel/assets/index-BhAVHuW8.js +1 -0
- package/src/assets/web-panel/assets/{index-B7osrXqy.js → index-Bissefox.js} +3 -3
- package/src/assets/web-panel/assets/{index-DW4BWjTk.js → index-BkvcEo4p.js} +1 -1
- package/src/assets/web-panel/assets/{index-DdkIhfi-.js → index-Bos5_aMV.js} +1 -1
- package/src/assets/web-panel/assets/{index-DTxNy0ZU.js → index-C1C9i79A.js} +1 -1
- package/src/assets/web-panel/assets/{index-QBSeNVO3.js → index-CAhHirVH.js} +1 -1
- package/src/assets/web-panel/assets/{index-D4LfPvWA.js → index-CNIxXMmN.js} +1 -1
- package/src/assets/web-panel/assets/{index-C_enk9I_.js → index-CW93tZDv.js} +1 -1
- package/src/assets/web-panel/assets/{index-WE08gMJP.js → index-CWEuZWCK.js} +1 -1
- package/src/assets/web-panel/assets/{index-r2VI1Ikk.js → index-Cp6e-hGw.js} +1 -1
- package/src/assets/web-panel/assets/{index-Dap9c3eD.js → index-CpjZhgNd.js} +1 -1
- package/src/assets/web-panel/assets/{index-DWdqx7zI.js → index-CzmQElZy.js} +1 -1
- package/src/assets/web-panel/assets/{index-BGbPnC5x.js → index-DCn8IcxV.js} +1 -1
- package/src/assets/web-panel/assets/{index-DiNUEpSQ.js → index-DE76D-kA.js} +1 -1
- package/src/assets/web-panel/assets/{index-DtkFqPVA.js → index-DaIz0NAW.js} +1 -1
- package/src/assets/web-panel/assets/{index-CGaXUsIs.js → index-Da_WBdLX.js} +1 -1
- package/src/assets/web-panel/assets/{index-B3ZX81mK.js → index-Dz49VOyQ.js} +1 -1
- package/src/assets/web-panel/assets/{index-BK3TiKVq.js → index-Q2SCX-MI.js} +1 -1
- package/src/assets/web-panel/assets/{index-CJmIQ9XR.js → index-RD6O-0C6.js} +1 -1
- package/src/assets/web-panel/assets/{index-CvrCCqJt.js → index-SQqOlR3d.js} +1 -1
- package/src/assets/web-panel/assets/{index-BI5hP_qy.js → index-ZFbps9po.js} +1 -1
- package/src/assets/web-panel/assets/{index-DeSgw-WV.js → index-ZxtP8jG7.js} +1 -1
- package/src/assets/web-panel/assets/{index-DMGmg497.js → index-bP_VlDb4.js} +1 -1
- package/src/assets/web-panel/assets/{index-BfFCQHL3.js → index-kf57LJ93.js} +1 -1
- package/src/assets/web-panel/assets/{index-OSzjTzGM.js → index-oxRrmFO9.js} +1 -1
- package/src/assets/web-panel/assets/{index-C2JvmYRc.js → index-p1_8SbI1.js} +1 -1
- package/src/assets/web-panel/assets/{initDefaultProps-Dd7n7VgI.js → initDefaultProps-BWJktcg8.js} +1 -1
- package/src/assets/web-panel/assets/{motion-BwxouRw1.js → motion-BEXBzUFV.js} +1 -1
- package/src/assets/web-panel/assets/{move-DObN0PG9.js → move-BUKwn2sx.js} +1 -1
- package/src/assets/web-panel/assets/{mtc-parser-B3nOUQc7.js → mtc-parser-FTI7btqQ.js} +1 -1
- package/src/assets/web-panel/assets/{omit-o9WU2Nbl.js → omit-CsLtSPH3.js} +1 -1
- package/src/assets/web-panel/assets/parsers-YNLm7sQ0.js +4 -0
- package/src/assets/web-panel/assets/{pickAttrs-CL_vvfQ_.js → pickAttrs-DWZxsT_S.js} +1 -1
- package/src/assets/web-panel/assets/{placementArrow-CYrM2tyK.js → placementArrow-Bcfs43CX.js} +1 -1
- package/src/assets/web-panel/assets/{responsiveObserve-k_06cQ96.js → responsiveObserve-a9bcC1p-.js} +1 -1
- package/src/assets/web-panel/assets/{slide-CPKG0pxS.js → slide-BDTzgOXN.js} +1 -1
- package/src/assets/web-panel/assets/{statusUtils-BDDEeLTw.js → statusUtils-Iq1j_HUg.js} +1 -1
- package/src/assets/web-panel/assets/{styleChecker-Cmb7RTh7.js → styleChecker-DAZEYxeh.js} +1 -1
- package/src/assets/web-panel/assets/{useFlexGapSupport-e2IedC4N.js → useFlexGapSupport-DVkjyDrh.js} +1 -1
- package/src/assets/web-panel/assets/{useFs-CE_gxVnm.js → useFs-wXTpNTHt.js} +1 -1
- package/src/assets/web-panel/assets/{usePersonalDataHub-BkMIIKMz.js → usePersonalDataHub-CtQM_aZr.js} +1 -1
- package/src/assets/web-panel/assets/{vnode-DW886XV1.js → vnode-hjEoG85v.js} +1 -1
- package/src/assets/web-panel/assets/{zoom-Dwo_adaf.js → zoom-BHVpyKg_.js} +1 -1
- package/src/assets/web-panel/index.html +1 -1
- package/src/command-help-index.json +3 -3
- package/src/commands/agent.js +14 -2
- package/src/commands/artifacts.js +235 -0
- package/src/commands/background-session.js +100 -10
- package/src/commands/cowork.js +86 -57
- package/src/commands/mcp.js +5 -0
- package/src/commands/plugin.js +145 -29
- package/src/commands/remote-control.js +295 -110
- package/src/commands/session-location.js +20 -1
- package/src/commands/session-message.js +171 -0
- package/src/commands/session.js +75 -69
- package/src/data/changelog.json +29 -3
- package/src/gateways/ws/message-dispatcher.js +57 -0
- package/src/harness/mcp-client.js +141 -10
- package/src/lib/artifact-access-ledger.js +1 -0
- package/src/lib/artifact-deletion-ledger.js +1 -0
- package/src/lib/artifact-recovery.js +970 -0
- package/src/lib/artifact-workbench-projection.js +171 -0
- package/src/lib/background-session-transport.js +153 -27
- package/src/lib/dynamic-workflow-runtime.js +408 -1
- package/src/lib/execution-location-target.js +4 -2
- package/src/lib/mcp-client.js +2 -0
- package/src/lib/mcp-headers-helper.js +47 -5
- package/src/lib/mcp-oauth.js +55 -6
- package/src/lib/mcp-stdio-executable-identity.js +16 -0
- package/src/lib/mcp-stdio-native-code-policy.js +74 -0
- package/src/lib/mcp-stdio-package-materialization.js +12 -3
- package/src/lib/mcp-tls.js +230 -0
- package/src/lib/output-styles.js +12 -0
- package/src/lib/plugin-runtime/install.js +614 -71
- package/src/lib/plugin-runtime/marketplace-archive-source.js +331 -0
- package/src/lib/plugin-runtime/marketplace-catalog.js +52 -6
- package/src/lib/plugin-runtime/marketplace-remote-artifacts.js +1 -0
- package/src/lib/plugin-runtime/marketplace-source-adapter.js +163 -0
- package/src/lib/plugin-runtime/marketplace-source-cache.js +11 -1
- package/src/lib/plugin-runtime/remote-source.js +435 -55
- package/src/lib/plugin-security.js +393 -41
- package/src/lib/plugin-source-identity.js +850 -0
- package/src/lib/process-execution-broker/index.js +112 -0
- package/src/lib/process-execution-broker/platform-sandbox.js +57 -3
- package/src/lib/process-execution-broker/workspace-transaction.js +26 -0
- package/src/lib/remote-approval-bridge.js +136 -46
- package/src/lib/remote-control.js +217 -31
- package/src/lib/session-message-fabric.js +1105 -0
- package/src/lib/session-projection.js +171 -12
- package/src/lib/session-runtime-retention.js +155 -0
- package/src/lib/settings-loader.cjs +29 -0
- package/src/repl/agent-repl.js +51 -14
- package/src/repl/permission-prompt.js +31 -8
- package/src/repl/repl-keybindings.js +55 -0
- package/src/runtime/agent-core.js +63 -2
- package/src/runtime/headless-runner.js +47 -66
- package/src/runtime/headless-stream.js +18 -24
- package/src/runtime/mcp-config.js +2 -0
- package/src/runtime/policies/agent-policy.js +2 -0
- package/src/assets/web-panel/assets/ChatBubbleRenderer-xijcWkJe.js +0 -1
- package/src/assets/web-panel/assets/MarkdownRenderer-hDpWdiBc.js +0 -1
- package/src/assets/web-panel/assets/Projects-BC9KSDM6.js +0 -1
- package/src/assets/web-panel/assets/Providers-Dz-CAYYU.js +0 -1
- package/src/assets/web-panel/assets/devWarning-DJZ0gAMu.js +0 -1
- package/src/assets/web-panel/assets/index-Bizb4hkw.js +0 -1
- package/src/assets/web-panel/assets/index-OHSl-xCh.js +0 -1
- package/src/assets/web-panel/assets/parsers-UEvh_ShA.js +0 -4
|
@@ -71,6 +71,7 @@ const MAX_CALLS_PER_EFFECT = 128;
|
|
|
71
71
|
const MAX_INPUT_REQUESTS = 64;
|
|
72
72
|
const MAX_INPUT_OPTIONS = 32;
|
|
73
73
|
const MAX_INPUT_RESPONSE_BYTES = 64 * 1024;
|
|
74
|
+
const MAX_WORKBENCH_RUN_FILES = 1024;
|
|
74
75
|
const MAX_PROJECTED_ARTIFACTS_PER_EFFECT = 256;
|
|
75
76
|
const MAX_PROVIDER_TOKEN_COUNT = 1_000_000_000;
|
|
76
77
|
const MAX_PROVIDER_USD_RATE_PER_MILLION = 1_000_000;
|
|
@@ -1518,6 +1519,31 @@ export function verifyDynamicWorkflowRuntimeState(value) {
|
|
|
1518
1519
|
) {
|
|
1519
1520
|
issues.push("authority-binding-invalid");
|
|
1520
1521
|
}
|
|
1522
|
+
if (state.executionBudget !== undefined && state.executionBudget !== null) {
|
|
1523
|
+
const budget = state.executionBudget;
|
|
1524
|
+
const fields = [
|
|
1525
|
+
"maxExpandedTasks",
|
|
1526
|
+
"maxParallel",
|
|
1527
|
+
"maxTokens",
|
|
1528
|
+
"maxUsd",
|
|
1529
|
+
"maxDurationMs",
|
|
1530
|
+
];
|
|
1531
|
+
const validBudget =
|
|
1532
|
+
budget &&
|
|
1533
|
+
typeof budget === "object" &&
|
|
1534
|
+
!Array.isArray(budget) &&
|
|
1535
|
+
Object.keys(budget).length === fields.length &&
|
|
1536
|
+
fields.every((field) => Object.hasOwn(budget, field)) &&
|
|
1537
|
+
fields.every((field) => {
|
|
1538
|
+
const amount = budget[field];
|
|
1539
|
+
if (amount === null) return true;
|
|
1540
|
+
if (!Number.isFinite(amount) || amount < 0) return false;
|
|
1541
|
+
return (
|
|
1542
|
+
field === "maxUsd" || (Number.isSafeInteger(amount) && amount >= 1)
|
|
1543
|
+
);
|
|
1544
|
+
});
|
|
1545
|
+
if (!validBudget) issues.push("execution-budget-invalid");
|
|
1546
|
+
}
|
|
1521
1547
|
if (
|
|
1522
1548
|
!Number.isSafeInteger(state.revision) ||
|
|
1523
1549
|
state.revision < 1 ||
|
|
@@ -1981,6 +2007,11 @@ function stateBindings(execution, runId) {
|
|
|
1981
2007
|
"workflow execution policy",
|
|
1982
2008
|
64 * 1024,
|
|
1983
2009
|
),
|
|
2010
|
+
executionBudget: snapshotJson(
|
|
2011
|
+
execution.workflow?.facade?.budget || null,
|
|
2012
|
+
"workflow execution budget",
|
|
2013
|
+
16 * 1024,
|
|
2014
|
+
),
|
|
1984
2015
|
};
|
|
1985
2016
|
}
|
|
1986
2017
|
|
|
@@ -1994,7 +2025,10 @@ function bindingsMatch(state, bindings) {
|
|
|
1994
2025
|
"executionAuthoritySessionId",
|
|
1995
2026
|
].every((field) => state[field] === bindings[field]) &&
|
|
1996
2027
|
canonicalJson(state.executionPolicy) ===
|
|
1997
|
-
canonicalJson(bindings.executionPolicy)
|
|
2028
|
+
canonicalJson(bindings.executionPolicy) &&
|
|
2029
|
+
(state.executionBudget === undefined ||
|
|
2030
|
+
canonicalJson(state.executionBudget) ===
|
|
2031
|
+
canonicalJson(bindings.executionBudget))
|
|
1998
2032
|
);
|
|
1999
2033
|
}
|
|
2000
2034
|
|
|
@@ -6045,3 +6079,376 @@ export function projectDynamicWorkflowRuntime(stateOrPath, options = {}) {
|
|
|
6045
6079
|
stateDigest: state.stateDigest,
|
|
6046
6080
|
});
|
|
6047
6081
|
}
|
|
6082
|
+
|
|
6083
|
+
function checkpointRecoverySummary(state, options = {}) {
|
|
6084
|
+
const checkpointStore =
|
|
6085
|
+
options.checkpointStore || new WorkspaceTransactionManager();
|
|
6086
|
+
let prepared = 0;
|
|
6087
|
+
let terminal = 0;
|
|
6088
|
+
let pending = 0;
|
|
6089
|
+
let unavailable = 0;
|
|
6090
|
+
for (const effect of state.effects) {
|
|
6091
|
+
if (effect.status !== "pending") continue;
|
|
6092
|
+
for (const call of effect.calls || []) {
|
|
6093
|
+
if (
|
|
6094
|
+
call.kind !== "tool" ||
|
|
6095
|
+
call.status !== "started" ||
|
|
6096
|
+
Object.hasOwn(call, "artifactReadback") ||
|
|
6097
|
+
!validCheckpointBinding(call.checkpointBinding)
|
|
6098
|
+
) {
|
|
6099
|
+
continue;
|
|
6100
|
+
}
|
|
6101
|
+
prepared += 1;
|
|
6102
|
+
try {
|
|
6103
|
+
if (
|
|
6104
|
+
checkpointReadbackFromPreparedBinding(
|
|
6105
|
+
call.checkpointBinding,
|
|
6106
|
+
checkpointStore,
|
|
6107
|
+
) === null
|
|
6108
|
+
) {
|
|
6109
|
+
pending += 1;
|
|
6110
|
+
} else {
|
|
6111
|
+
terminal += 1;
|
|
6112
|
+
}
|
|
6113
|
+
} catch {
|
|
6114
|
+
unavailable += 1;
|
|
6115
|
+
}
|
|
6116
|
+
}
|
|
6117
|
+
}
|
|
6118
|
+
return Object.freeze({ prepared, terminal, pending, unavailable });
|
|
6119
|
+
}
|
|
6120
|
+
|
|
6121
|
+
function observedBudgetStatus(observed, limit, complete) {
|
|
6122
|
+
if (!Number.isFinite(limit) || !Number.isFinite(observed) || !complete) {
|
|
6123
|
+
return "unknown";
|
|
6124
|
+
}
|
|
6125
|
+
return observed > limit ? "exceeded" : "within";
|
|
6126
|
+
}
|
|
6127
|
+
|
|
6128
|
+
const WORKFLOW_RECOVERY_BACKOFF_MS = Object.freeze([
|
|
6129
|
+
15_000,
|
|
6130
|
+
60_000,
|
|
6131
|
+
5 * 60_000,
|
|
6132
|
+
15 * 60_000,
|
|
6133
|
+
]);
|
|
6134
|
+
|
|
6135
|
+
function dynamicWorkflowRecoveryPolicy(state, recovery) {
|
|
6136
|
+
const pendingInput = runtimeInputRequests(state).filter(
|
|
6137
|
+
(request) => request.status === "pending",
|
|
6138
|
+
).length;
|
|
6139
|
+
const pendingEffects = state.effects.filter(
|
|
6140
|
+
(effect) => effect.status === "pending",
|
|
6141
|
+
);
|
|
6142
|
+
const uncertainEffects = pendingEffects.filter(
|
|
6143
|
+
(effect) => !isKnownUndispatched(effect),
|
|
6144
|
+
).length;
|
|
6145
|
+
let risk = "none";
|
|
6146
|
+
let severity = "info";
|
|
6147
|
+
let recommendedAction = "none";
|
|
6148
|
+
let requiresApproval = false;
|
|
6149
|
+
let automaticallyExecutable = false;
|
|
6150
|
+
|
|
6151
|
+
if (recovery.terminal > 0) {
|
|
6152
|
+
risk = "terminal_checkpoint_recovery";
|
|
6153
|
+
severity = "warning";
|
|
6154
|
+
recommendedAction = "recover";
|
|
6155
|
+
requiresApproval = true;
|
|
6156
|
+
automaticallyExecutable = true;
|
|
6157
|
+
} else if (pendingInput > 0) {
|
|
6158
|
+
risk = "input_required";
|
|
6159
|
+
severity = "warning";
|
|
6160
|
+
recommendedAction = "reply";
|
|
6161
|
+
requiresApproval = true;
|
|
6162
|
+
} else if (uncertainEffects > 0) {
|
|
6163
|
+
risk = "operator_adjudication_required";
|
|
6164
|
+
severity = "critical";
|
|
6165
|
+
recommendedAction = "reconcile";
|
|
6166
|
+
requiresApproval = true;
|
|
6167
|
+
} else if (state.status === "pause_requested") {
|
|
6168
|
+
risk = "settlement_barrier_pending";
|
|
6169
|
+
severity = "info";
|
|
6170
|
+
recommendedAction = "wait";
|
|
6171
|
+
} else if (
|
|
6172
|
+
["paused", "failed", "blocked"].includes(state.status) &&
|
|
6173
|
+
pendingEffects.length === 0
|
|
6174
|
+
) {
|
|
6175
|
+
risk = "restart_ready";
|
|
6176
|
+
severity = "warning";
|
|
6177
|
+
recommendedAction = "resume";
|
|
6178
|
+
requiresApproval = true;
|
|
6179
|
+
automaticallyExecutable = true;
|
|
6180
|
+
}
|
|
6181
|
+
|
|
6182
|
+
const notificationKey = digest(
|
|
6183
|
+
"chainlesschain.dynamic-workflow.recovery-notification.v1\0",
|
|
6184
|
+
{
|
|
6185
|
+
runId: state.runId,
|
|
6186
|
+
revision: state.revision,
|
|
6187
|
+
stateDigest: state.stateDigest,
|
|
6188
|
+
risk,
|
|
6189
|
+
recommendedAction,
|
|
6190
|
+
},
|
|
6191
|
+
);
|
|
6192
|
+
return Object.freeze({
|
|
6193
|
+
schema: "cc-dynamic-workflow-recovery-policy/v1",
|
|
6194
|
+
risk,
|
|
6195
|
+
severity,
|
|
6196
|
+
recommendedAction,
|
|
6197
|
+
requiresApproval,
|
|
6198
|
+
automaticallyExecutable,
|
|
6199
|
+
unattendedMutationAllowed: false,
|
|
6200
|
+
pendingInput,
|
|
6201
|
+
pendingEffects: pendingEffects.length,
|
|
6202
|
+
uncertainEffects,
|
|
6203
|
+
notification: Object.freeze({
|
|
6204
|
+
key: notificationKey,
|
|
6205
|
+
backoffMs: WORKFLOW_RECOVERY_BACKOFF_MS,
|
|
6206
|
+
resetOnStateDigestChange: true,
|
|
6207
|
+
}),
|
|
6208
|
+
});
|
|
6209
|
+
}
|
|
6210
|
+
|
|
6211
|
+
/**
|
|
6212
|
+
* Bounded, content-free view used by the cross-IDE Sessions Workbench. Tool
|
|
6213
|
+
* arguments, prompts, provider output, result bodies, and filesystem bindings
|
|
6214
|
+
* deliberately never cross this projection boundary.
|
|
6215
|
+
*/
|
|
6216
|
+
export function projectDynamicWorkflowWorkbenchState(
|
|
6217
|
+
stateOrPath,
|
|
6218
|
+
options = {},
|
|
6219
|
+
) {
|
|
6220
|
+
const state =
|
|
6221
|
+
typeof stateOrPath === "string"
|
|
6222
|
+
? readDynamicWorkflowRuntimeState(stateOrPath)
|
|
6223
|
+
: verifyDynamicWorkflowRuntimeState(stateOrPath);
|
|
6224
|
+
const observability = projectDynamicWorkflowObservability(state);
|
|
6225
|
+
const recovery = checkpointRecoverySummary(state, options);
|
|
6226
|
+
const recoveryPolicy = dynamicWorkflowRecoveryPolicy(state, recovery);
|
|
6227
|
+
const recentEffect = state.effects.at(-1) || null;
|
|
6228
|
+
const recentCall = recentEffect?.calls?.at(-1) || null;
|
|
6229
|
+
const lastTransition = state.lineage.at(-1) || null;
|
|
6230
|
+
const budget = state.executionBudget || null;
|
|
6231
|
+
const providerTokens = observability.tokens.providerReported?.totalTokens;
|
|
6232
|
+
const observedTokens = Number.isFinite(providerTokens)
|
|
6233
|
+
? providerTokens
|
|
6234
|
+
: observability.tokens.missingEffects === 0
|
|
6235
|
+
? observability.tokens.estimated
|
|
6236
|
+
: null;
|
|
6237
|
+
const tokenComplete =
|
|
6238
|
+
Number.isFinite(providerTokens) ||
|
|
6239
|
+
observability.tokens.missingEffects === 0;
|
|
6240
|
+
const observedUsd = Number.isFinite(observability.cost.estimatedUsd)
|
|
6241
|
+
? observability.cost.estimatedUsd
|
|
6242
|
+
: null;
|
|
6243
|
+
const costComplete =
|
|
6244
|
+
observability.cost.providerCalls === observability.cost.pricedCalls;
|
|
6245
|
+
const observedDurationMs = observability.duration.totalMs;
|
|
6246
|
+
const durationComplete = observability.effects.pending === 0;
|
|
6247
|
+
const budgetStatus = {
|
|
6248
|
+
tokens: observedBudgetStatus(
|
|
6249
|
+
observedTokens,
|
|
6250
|
+
budget?.maxTokens,
|
|
6251
|
+
tokenComplete,
|
|
6252
|
+
),
|
|
6253
|
+
usd: observedBudgetStatus(observedUsd, budget?.maxUsd, costComplete),
|
|
6254
|
+
duration: observedBudgetStatus(
|
|
6255
|
+
observedDurationMs,
|
|
6256
|
+
budget?.maxDurationMs,
|
|
6257
|
+
durationComplete,
|
|
6258
|
+
),
|
|
6259
|
+
};
|
|
6260
|
+
const overallBudgetStatus = Object.values(budgetStatus).includes("exceeded")
|
|
6261
|
+
? "exceeded"
|
|
6262
|
+
: Object.values(budgetStatus).includes("unknown")
|
|
6263
|
+
? "unknown"
|
|
6264
|
+
: "within";
|
|
6265
|
+
|
|
6266
|
+
return Object.freeze({
|
|
6267
|
+
schema: "cc-dynamic-workflow-workbench-state/v1",
|
|
6268
|
+
runId: state.runId,
|
|
6269
|
+
workflowId: state.workflowId,
|
|
6270
|
+
status: state.status,
|
|
6271
|
+
revision: state.revision,
|
|
6272
|
+
stateDigest: state.stateDigest,
|
|
6273
|
+
definitionDigest: state.definitionDigest,
|
|
6274
|
+
admissionDigest: state.admissionDigest,
|
|
6275
|
+
executionAuthoritySessionId: state.executionAuthoritySessionId,
|
|
6276
|
+
createdAt: state.createdAt,
|
|
6277
|
+
updatedAt: state.updatedAt,
|
|
6278
|
+
phase: Object.freeze({
|
|
6279
|
+
status: state.status,
|
|
6280
|
+
transition: lastTransition?.type || "run-created",
|
|
6281
|
+
at: lastTransition?.at || state.createdAt,
|
|
6282
|
+
}),
|
|
6283
|
+
agents: Object.freeze({
|
|
6284
|
+
requested: observability.effects.requested,
|
|
6285
|
+
settled: observability.effects.settled,
|
|
6286
|
+
pending: observability.effects.pending,
|
|
6287
|
+
completed: observability.effects.completedTasks,
|
|
6288
|
+
failed: observability.effects.failedTasks,
|
|
6289
|
+
}),
|
|
6290
|
+
input: Object.freeze({
|
|
6291
|
+
requested: runtimeInputRequests(state).length,
|
|
6292
|
+
pending: runtimeInputRequests(state).filter(
|
|
6293
|
+
(request) => request.status === "pending",
|
|
6294
|
+
).length,
|
|
6295
|
+
}),
|
|
6296
|
+
budget: Object.freeze({
|
|
6297
|
+
limits: budget,
|
|
6298
|
+
observed: Object.freeze({
|
|
6299
|
+
tokens: observedTokens,
|
|
6300
|
+
usd: observedUsd,
|
|
6301
|
+
durationMs: observedDurationMs,
|
|
6302
|
+
}),
|
|
6303
|
+
status: Object.freeze(budgetStatus),
|
|
6304
|
+
overall: overallBudgetStatus,
|
|
6305
|
+
}),
|
|
6306
|
+
artifacts: Object.freeze({ count: observability.artifacts.count }),
|
|
6307
|
+
checkpoints: Object.freeze({ count: observability.checkpoints.count }),
|
|
6308
|
+
recovery,
|
|
6309
|
+
recoveryPolicy,
|
|
6310
|
+
recent: recentEffect
|
|
6311
|
+
? Object.freeze({
|
|
6312
|
+
effectId: recentEffect.id,
|
|
6313
|
+
stepId: recentEffect.stepId,
|
|
6314
|
+
status: recentEffect.status,
|
|
6315
|
+
taskStatus:
|
|
6316
|
+
recentEffect.result?.status === "completed" ||
|
|
6317
|
+
recentEffect.result?.status === "failed"
|
|
6318
|
+
? recentEffect.result.status
|
|
6319
|
+
: null,
|
|
6320
|
+
requestedAt: recentEffect.requestedAt,
|
|
6321
|
+
settledAt: recentEffect.settledAt || null,
|
|
6322
|
+
resultDigest: recentEffect.resultDigest || null,
|
|
6323
|
+
call: recentCall
|
|
6324
|
+
? Object.freeze({
|
|
6325
|
+
id: recentCall.id,
|
|
6326
|
+
kind: recentCall.kind,
|
|
6327
|
+
name: recentCall.name,
|
|
6328
|
+
status: recentCall.status,
|
|
6329
|
+
settlementCode: recentCall.settlementCode,
|
|
6330
|
+
settledAt: recentCall.settledAt,
|
|
6331
|
+
})
|
|
6332
|
+
: null,
|
|
6333
|
+
})
|
|
6334
|
+
: null,
|
|
6335
|
+
});
|
|
6336
|
+
}
|
|
6337
|
+
|
|
6338
|
+
/** Securely discover durable workflow state files below one project root. */
|
|
6339
|
+
export function listDynamicWorkflowWorkbenchStates(cwd, options = {}) {
|
|
6340
|
+
const limit = Math.max(1, Math.min(256, Number(options.limit) || 100));
|
|
6341
|
+
const projectionOptions = Object.hasOwn(options, "checkpointStore")
|
|
6342
|
+
? options
|
|
6343
|
+
: { ...options, checkpointStore: new WorkspaceTransactionManager() };
|
|
6344
|
+
const directory = path.join(
|
|
6345
|
+
path.resolve(cwd),
|
|
6346
|
+
".chainlesschain",
|
|
6347
|
+
"cowork",
|
|
6348
|
+
"workflow-runs",
|
|
6349
|
+
);
|
|
6350
|
+
let stat;
|
|
6351
|
+
try {
|
|
6352
|
+
stat = fs.lstatSync(directory);
|
|
6353
|
+
} catch (error) {
|
|
6354
|
+
if (error?.code === "ENOENT") {
|
|
6355
|
+
return Object.freeze({ runs: Object.freeze([]), invalidCount: 0 });
|
|
6356
|
+
}
|
|
6357
|
+
throw error;
|
|
6358
|
+
}
|
|
6359
|
+
if (!stat.isDirectory() || stat.isSymbolicLink()) {
|
|
6360
|
+
throw new Error("dynamic workflow runtime directory is not trusted");
|
|
6361
|
+
}
|
|
6362
|
+
const entries = fs.readdirSync(directory, { withFileTypes: true });
|
|
6363
|
+
if (entries.length > MAX_WORKBENCH_RUN_FILES) {
|
|
6364
|
+
throw new Error("dynamic workflow runtime directory exceeds safe limits");
|
|
6365
|
+
}
|
|
6366
|
+
const runs = [];
|
|
6367
|
+
let invalidCount = 0;
|
|
6368
|
+
for (const entry of entries) {
|
|
6369
|
+
if (
|
|
6370
|
+
!entry.isFile() ||
|
|
6371
|
+
entry.isSymbolicLink() ||
|
|
6372
|
+
!/^[A-Za-z0-9][A-Za-z0-9._-]{0,255}\.json$/u.test(entry.name)
|
|
6373
|
+
) {
|
|
6374
|
+
continue;
|
|
6375
|
+
}
|
|
6376
|
+
const runId = entry.name.slice(0, -".json".length);
|
|
6377
|
+
try {
|
|
6378
|
+
const statePath = dynamicWorkflowRunStatePath(cwd, runId);
|
|
6379
|
+
const state = readDynamicWorkflowRuntimeState(statePath);
|
|
6380
|
+
if (state.runId !== runId) throw new Error("run identity mismatch");
|
|
6381
|
+
runs.push(projectDynamicWorkflowWorkbenchState(state, projectionOptions));
|
|
6382
|
+
} catch {
|
|
6383
|
+
invalidCount += 1;
|
|
6384
|
+
}
|
|
6385
|
+
}
|
|
6386
|
+
runs.sort(
|
|
6387
|
+
(left, right) =>
|
|
6388
|
+
(Date.parse(right.updatedAt) || 0) - (Date.parse(left.updatedAt) || 0) ||
|
|
6389
|
+
left.runId.localeCompare(right.runId),
|
|
6390
|
+
);
|
|
6391
|
+
return Object.freeze({
|
|
6392
|
+
runs: Object.freeze(runs.slice(0, limit)),
|
|
6393
|
+
invalidCount,
|
|
6394
|
+
});
|
|
6395
|
+
}
|
|
6396
|
+
|
|
6397
|
+
/** Deterministic, non-mutating batch inventory for startup/periodic recovery. */
|
|
6398
|
+
export function buildDynamicWorkflowRecoveryPlan(cwd, options = {}) {
|
|
6399
|
+
const discovered = listDynamicWorkflowWorkbenchStates(cwd, options);
|
|
6400
|
+
const policy = Object.freeze({
|
|
6401
|
+
schema: "cc-dynamic-workflow-recovery-policy-set/v1",
|
|
6402
|
+
unattendedMutationAllowed: false,
|
|
6403
|
+
approvalRequiredFor: Object.freeze([
|
|
6404
|
+
"recover",
|
|
6405
|
+
"resume",
|
|
6406
|
+
"reply",
|
|
6407
|
+
"reconcile",
|
|
6408
|
+
]),
|
|
6409
|
+
backoffMs: WORKFLOW_RECOVERY_BACKOFF_MS,
|
|
6410
|
+
});
|
|
6411
|
+
const items = discovered.runs.map((run) =>
|
|
6412
|
+
Object.freeze({
|
|
6413
|
+
runId: run.runId,
|
|
6414
|
+
workflowId: run.workflowId,
|
|
6415
|
+
revision: run.revision,
|
|
6416
|
+
stateDigest: run.stateDigest,
|
|
6417
|
+
status: run.status,
|
|
6418
|
+
updatedAt: run.updatedAt,
|
|
6419
|
+
risk: run.recoveryPolicy.risk,
|
|
6420
|
+
severity: run.recoveryPolicy.severity,
|
|
6421
|
+
recommendedAction: run.recoveryPolicy.recommendedAction,
|
|
6422
|
+
requiresApproval: run.recoveryPolicy.requiresApproval,
|
|
6423
|
+
automaticallyExecutable: run.recoveryPolicy.automaticallyExecutable,
|
|
6424
|
+
notificationKey: run.recoveryPolicy.notification.key,
|
|
6425
|
+
recovery: run.recovery,
|
|
6426
|
+
}),
|
|
6427
|
+
);
|
|
6428
|
+
const summary = Object.freeze({
|
|
6429
|
+
total: items.length,
|
|
6430
|
+
invalid: discovered.invalidCount,
|
|
6431
|
+
attention: items.filter((item) => item.risk !== "none").length,
|
|
6432
|
+
critical: items.filter((item) => item.severity === "critical").length,
|
|
6433
|
+
approvalRequired: items.filter((item) => item.requiresApproval).length,
|
|
6434
|
+
automaticallyExecutable: items.filter(
|
|
6435
|
+
(item) => item.automaticallyExecutable,
|
|
6436
|
+
).length,
|
|
6437
|
+
});
|
|
6438
|
+
const planMaterial = { policy, summary, items };
|
|
6439
|
+
return Object.freeze({
|
|
6440
|
+
schema: "cc-dynamic-workflow-recovery-plan/v1",
|
|
6441
|
+
authority: "cli",
|
|
6442
|
+
mode: "dry-run",
|
|
6443
|
+
generatedAt: isoNow(options.now),
|
|
6444
|
+
projectDigest: digest(
|
|
6445
|
+
"chainlesschain.dynamic-workflow.recovery-project.v1\0",
|
|
6446
|
+
path.resolve(cwd),
|
|
6447
|
+
),
|
|
6448
|
+
planDigest: digest(
|
|
6449
|
+
"chainlesschain.dynamic-workflow.recovery-plan.v1\0",
|
|
6450
|
+
planMaterial,
|
|
6451
|
+
),
|
|
6452
|
+
...planMaterial,
|
|
6453
|
+
});
|
|
6454
|
+
}
|
|
@@ -814,9 +814,11 @@ function transferTargetSessionStore(
|
|
|
814
814
|
receipt.targetFactsDigest !== receiptMaterial.targetFactsDigest ||
|
|
815
815
|
receipt.profileDigest !== receiptMaterial.profileDigest ||
|
|
816
816
|
receipt.targetEvidenceId !== receiptMaterial.targetEvidenceId ||
|
|
817
|
+
// Target prepare records a fresh observedAt, so its attestation digest is
|
|
818
|
+
// expected to differ from the earlier source-side probe. The receipt is
|
|
819
|
+
// rehashed here and the next target `location show` validates that digest
|
|
820
|
+
// against the canonical handoff binding before resume.
|
|
817
821
|
!SHA256_RE.test(receipt.attestationDigest || "") ||
|
|
818
|
-
(receipt.handoffAppended === true &&
|
|
819
|
-
receipt.attestationDigest !== attestation.attestationDigest) ||
|
|
820
822
|
typeof receipt.replicaInstalled !== "boolean" ||
|
|
821
823
|
typeof receipt.handoffAppended !== "boolean" ||
|
|
822
824
|
receipt.receiptDigest !== expectedReceiptDigest
|
package/src/lib/mcp-client.js
CHANGED
|
@@ -17,8 +17,8 @@ const { buildExplicitHookShellInvocation } = hookShellCommand;
|
|
|
17
17
|
export const MCP_HEADERS_HELPER_TIMEOUT_MS = 10_000;
|
|
18
18
|
export const MCP_HEADERS_HELPER_MAX_OUTPUT_BYTES = 64 * 1024;
|
|
19
19
|
const MAX_HELPER_COMMAND_BYTES = 32 * 1024;
|
|
20
|
-
const
|
|
21
|
-
const
|
|
20
|
+
export const MCP_HEADERS_HELPER_MAX_HEADER_COUNT = 128;
|
|
21
|
+
export const MCP_HEADERS_HELPER_MAX_HEADER_VALUE_BYTES = 16 * 1024;
|
|
22
22
|
const HEADER_NAME = /^[!#$%&'*+.^_`|~0-9A-Za-z-]+$/;
|
|
23
23
|
const TRANSPORT_OWNED_HEADERS = new Set([
|
|
24
24
|
"accept",
|
|
@@ -37,6 +37,25 @@ const TRANSPORT_OWNED_HEADERS = new Set([
|
|
|
37
37
|
"upgrade",
|
|
38
38
|
]);
|
|
39
39
|
const PUBLIC_CONFIG_SCOPES = new Set(["local", "project", "user", "managed"]);
|
|
40
|
+
const HELPER_ENVIRONMENT_ALLOWLIST = new Set([
|
|
41
|
+
"COMSPEC",
|
|
42
|
+
"HOME",
|
|
43
|
+
"LANG",
|
|
44
|
+
"LC_ALL",
|
|
45
|
+
"LC_CTYPE",
|
|
46
|
+
"NODE_EXTRA_CA_CERTS",
|
|
47
|
+
"PATH",
|
|
48
|
+
"PATHEXT",
|
|
49
|
+
"SSL_CERT_DIR",
|
|
50
|
+
"SSL_CERT_FILE",
|
|
51
|
+
"SYSTEMROOT",
|
|
52
|
+
"TEMP",
|
|
53
|
+
"TMP",
|
|
54
|
+
"TMPDIR",
|
|
55
|
+
"USERPROFILE",
|
|
56
|
+
"WINDIR",
|
|
57
|
+
"XDG_CONFIG_HOME",
|
|
58
|
+
]);
|
|
40
59
|
|
|
41
60
|
function helperError(code, message) {
|
|
42
61
|
const error = new Error(message);
|
|
@@ -98,6 +117,28 @@ function normalizeHelperCommand(value) {
|
|
|
98
117
|
return value;
|
|
99
118
|
}
|
|
100
119
|
|
|
120
|
+
export function sanitizeMcpHeadersHelperEnvironment(environment = process.env) {
|
|
121
|
+
if (!environment || typeof environment !== "object" || isProxy(environment)) {
|
|
122
|
+
throw helperError(
|
|
123
|
+
"CC_MCP_HEADERS_HELPER_ENV_INVALID",
|
|
124
|
+
"MCP headersHelper environment must be a non-Proxy object",
|
|
125
|
+
);
|
|
126
|
+
}
|
|
127
|
+
const sanitized = {};
|
|
128
|
+
for (const key of Object.keys(environment)) {
|
|
129
|
+
if (!HELPER_ENVIRONMENT_ALLOWLIST.has(key.toUpperCase())) continue;
|
|
130
|
+
const descriptor = Object.getOwnPropertyDescriptor(environment, key);
|
|
131
|
+
if (!descriptor || !("value" in descriptor)) {
|
|
132
|
+
throw helperError(
|
|
133
|
+
"CC_MCP_HEADERS_HELPER_ENV_INVALID",
|
|
134
|
+
"MCP headersHelper environment values must be own data properties",
|
|
135
|
+
);
|
|
136
|
+
}
|
|
137
|
+
if (typeof descriptor.value === "string") sanitized[key] = descriptor.value;
|
|
138
|
+
}
|
|
139
|
+
return sanitized;
|
|
140
|
+
}
|
|
141
|
+
|
|
101
142
|
function canonicalHelperCwd(value, realpath = fs.realpathSync.native) {
|
|
102
143
|
if (
|
|
103
144
|
typeof value !== "string" ||
|
|
@@ -148,7 +189,7 @@ function normalizeHeaderMap(value, source) {
|
|
|
148
189
|
);
|
|
149
190
|
}
|
|
150
191
|
const entries = Object.entries(value);
|
|
151
|
-
if (entries.length >
|
|
192
|
+
if (entries.length > MCP_HEADERS_HELPER_MAX_HEADER_COUNT) {
|
|
152
193
|
throw helperError(
|
|
153
194
|
"CC_MCP_HEADERS_HELPER_OUTPUT_INVALID",
|
|
154
195
|
`MCP ${source} headers exceed the allowed entry count`,
|
|
@@ -160,7 +201,8 @@ function normalizeHeaderMap(value, source) {
|
|
|
160
201
|
!HEADER_NAME.test(name) ||
|
|
161
202
|
typeof headerValue !== "string" ||
|
|
162
203
|
hasInvalidHeaderValueCharacter(headerValue) ||
|
|
163
|
-
Buffer.byteLength(headerValue, "utf8") >
|
|
204
|
+
Buffer.byteLength(headerValue, "utf8") >
|
|
205
|
+
MCP_HEADERS_HELPER_MAX_HEADER_VALUE_BYTES ||
|
|
164
206
|
(source === "dynamic" && TRANSPORT_OWNED_HEADERS.has(name.toLowerCase()))
|
|
165
207
|
) {
|
|
166
208
|
throw helperError(
|
|
@@ -486,7 +528,7 @@ export function runMcpHeadersHelper(spec, deps = {}) {
|
|
|
486
528
|
),
|
|
487
529
|
);
|
|
488
530
|
const env = {
|
|
489
|
-
...(spec?.env || process.env),
|
|
531
|
+
...sanitizeMcpHeadersHelperEnvironment(spec?.env || process.env),
|
|
490
532
|
CLAUDE_CODE_MCP_SERVER_NAME: String(spec?.serverName || ""),
|
|
491
533
|
CLAUDE_CODE_MCP_SERVER_URL: String(spec?.serverUrl || ""),
|
|
492
534
|
...(spec?.pluginRoot
|
package/src/lib/mcp-oauth.js
CHANGED
|
@@ -52,6 +52,56 @@ export const _deps = {
|
|
|
52
52
|
withStoreLock,
|
|
53
53
|
};
|
|
54
54
|
|
|
55
|
+
/**
|
|
56
|
+
* Resolve the exact IPv4-loopback redirect used by both the listener and the
|
|
57
|
+
* OAuth wire exchange. Pre-registered clients need byte-for-byte redirect URI
|
|
58
|
+
* stability, so explicit values are accepted only in canonical form.
|
|
59
|
+
*/
|
|
60
|
+
export function resolveOAuthLoopbackRedirect({
|
|
61
|
+
redirectUri,
|
|
62
|
+
host = "127.0.0.1",
|
|
63
|
+
port = 53682,
|
|
64
|
+
redirectPath = "/callback",
|
|
65
|
+
} = {}) {
|
|
66
|
+
if (redirectUri != null && typeof redirectUri !== "string") {
|
|
67
|
+
throw new Error("OAuth redirectUri must be a string");
|
|
68
|
+
}
|
|
69
|
+
const candidate =
|
|
70
|
+
redirectUri == null ? `http://${host}:${port}${redirectPath}` : redirectUri;
|
|
71
|
+
let parsed;
|
|
72
|
+
try {
|
|
73
|
+
parsed = new URL(candidate);
|
|
74
|
+
} catch {
|
|
75
|
+
throw new Error("OAuth redirectUri must be a valid loopback URL");
|
|
76
|
+
}
|
|
77
|
+
const parsedPort = Number(parsed.port);
|
|
78
|
+
const canonical = `http://127.0.0.1:${parsed.port}${parsed.pathname}`;
|
|
79
|
+
if (
|
|
80
|
+
parsed.protocol !== "http:" ||
|
|
81
|
+
parsed.hostname !== "127.0.0.1" ||
|
|
82
|
+
!parsed.port ||
|
|
83
|
+
!Number.isInteger(parsedPort) ||
|
|
84
|
+
parsedPort < 1 ||
|
|
85
|
+
parsedPort > 65535 ||
|
|
86
|
+
parsed.username ||
|
|
87
|
+
parsed.password ||
|
|
88
|
+
parsed.search ||
|
|
89
|
+
parsed.hash ||
|
|
90
|
+
!parsed.pathname.startsWith("/") ||
|
|
91
|
+
candidate !== canonical
|
|
92
|
+
) {
|
|
93
|
+
throw new Error(
|
|
94
|
+
"OAuth redirectUri must be an exact canonical http://127.0.0.1:<port>/<path> URL",
|
|
95
|
+
);
|
|
96
|
+
}
|
|
97
|
+
return {
|
|
98
|
+
redirectUri: candidate,
|
|
99
|
+
host: "127.0.0.1",
|
|
100
|
+
port: parsedPort,
|
|
101
|
+
redirectPath: parsed.pathname,
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
|
|
55
105
|
/** Discovery/token requests retry ONCE after a transient error (CC 2.1.191). */
|
|
56
106
|
const MCP_OAUTH_RETRY_BACKOFF_MS = 300;
|
|
57
107
|
|
|
@@ -794,23 +844,22 @@ export function waitForCallback({
|
|
|
794
844
|
* Run the full interactive Authorization Code + PKCE flow and persist the token.
|
|
795
845
|
*
|
|
796
846
|
* @param {string} serverUrl
|
|
797
|
-
* @param {object} [opts] { scope, clientId, port=53682, host,
|
|
798
|
-
* timeout, writeOut }
|
|
847
|
+
* @param {object} [opts] { scope, clientId, redirectUri, port=53682, host,
|
|
848
|
+
* redirectPath, timeout, writeOut }
|
|
799
849
|
* @returns {Promise<{server, access_token, ...}>} the stored record
|
|
800
850
|
*/
|
|
801
851
|
export async function authorizeInteractive(serverUrl, opts = {}) {
|
|
802
852
|
const {
|
|
803
853
|
scope,
|
|
804
854
|
clientId: cfgClientId,
|
|
805
|
-
port = 53682,
|
|
806
|
-
host = "127.0.0.1",
|
|
807
|
-
redirectPath = "/callback",
|
|
808
855
|
timeout = 300_000,
|
|
809
856
|
writeOut = (s) => process.stdout.write(s),
|
|
810
857
|
} = opts;
|
|
811
858
|
|
|
859
|
+
const { redirectUri, host, port, redirectPath } =
|
|
860
|
+
resolveOAuthLoopbackRedirect(opts);
|
|
861
|
+
|
|
812
862
|
const metadata = await discoverAuthMetadata(serverUrl);
|
|
813
|
-
const redirectUri = `http://${host}:${port}${redirectPath}`;
|
|
814
863
|
|
|
815
864
|
let clientId = cfgClientId;
|
|
816
865
|
let clientSecret = null;
|
|
@@ -24,6 +24,10 @@ import {
|
|
|
24
24
|
reattestMcpStdioPackageMaterialization,
|
|
25
25
|
resolveMcpStdioPackageMaterialization,
|
|
26
26
|
} from "./mcp-stdio-package-materialization.js";
|
|
27
|
+
import {
|
|
28
|
+
isMcpStdioCapsuleNativeCodePolicy,
|
|
29
|
+
mcpStdioCapsuleNativeCodePolicyDigest,
|
|
30
|
+
} from "./mcp-stdio-native-code-policy.js";
|
|
27
31
|
import {
|
|
28
32
|
isMcpStdioCodeInjectionEnvironmentKey,
|
|
29
33
|
sanitizeMcpStdioHostEnvironment,
|
|
@@ -48,11 +52,13 @@ export const MCP_STDIO_RUNTIME_KIND_INVALID_CODE =
|
|
|
48
52
|
export const MCP_STDIO_CAPSULE_SANDBOX_CONTRACT_KIND =
|
|
49
53
|
"strict-mcp-node-capsule";
|
|
50
54
|
export const MCP_STDIO_CAPSULE_CODE_SNAPSHOT_BOUNDARY = "code-snapshot";
|
|
55
|
+
export const MCP_STDIO_CAPSULE_NATIVE_ADDON_BOUNDARY = "native-addon-loading";
|
|
51
56
|
export const MCP_STDIO_CAPSULE_REQUIRED_BOUNDARIES = Object.freeze([
|
|
52
57
|
MCP_STDIO_CAPSULE_CODE_SNAPSHOT_BOUNDARY,
|
|
53
58
|
"filesystem",
|
|
54
59
|
"network",
|
|
55
60
|
"process-tree",
|
|
61
|
+
MCP_STDIO_CAPSULE_NATIVE_ADDON_BOUNDARY,
|
|
56
62
|
]);
|
|
57
63
|
|
|
58
64
|
const STORE_LABEL = "MCP stdio executable identity";
|
|
@@ -852,6 +858,10 @@ function issueCapsuleSandboxExecutionContract({
|
|
|
852
858
|
materialization,
|
|
853
859
|
}) {
|
|
854
860
|
if (!materialization) return null;
|
|
861
|
+
const nativeCodePolicy = materialization.identity?.capsule?.nativeCodePolicy;
|
|
862
|
+
if (!isMcpStdioCapsuleNativeCodePolicy(nativeCodePolicy)) {
|
|
863
|
+
throw new Error("MCP materialized capsule native-code policy is invalid");
|
|
864
|
+
}
|
|
855
865
|
const capsuleRoot = attestCapsuleRoot(materialization.capsuleRoot);
|
|
856
866
|
const entryIdentity = attestation.identity.entrypoints.find(
|
|
857
867
|
(entry) => entry.argIndex === 0,
|
|
@@ -878,6 +888,7 @@ function issueCapsuleSandboxExecutionContract({
|
|
|
878
888
|
attestation.identity.command,
|
|
879
889
|
path.resolve(process.execPath),
|
|
880
890
|
),
|
|
891
|
+
nativeCodePolicy: Object.freeze({ ...nativeCodePolicy }),
|
|
881
892
|
});
|
|
882
893
|
issuedCapsuleSandboxContracts.set(
|
|
883
894
|
contract,
|
|
@@ -888,6 +899,8 @@ function issueCapsuleSandboxExecutionContract({
|
|
|
888
899
|
cwd: capsuleRoot.realPath,
|
|
889
900
|
identityDigest: attestation.identityDigest,
|
|
890
901
|
requiredBoundaries: MCP_STDIO_CAPSULE_REQUIRED_BOUNDARIES,
|
|
902
|
+
nativeCodePolicyDigest:
|
|
903
|
+
mcpStdioCapsuleNativeCodePolicyDigest(nativeCodePolicy),
|
|
891
904
|
}),
|
|
892
905
|
);
|
|
893
906
|
return contract;
|
|
@@ -918,6 +931,9 @@ export function consumeMcpStdioCapsuleSandboxExecutionContract(
|
|
|
918
931
|
issued.requiredBoundaries.every((boundary) =>
|
|
919
932
|
requiredBoundaries.includes(boundary),
|
|
920
933
|
) &&
|
|
934
|
+
isMcpStdioCapsuleNativeCodePolicy(contract.nativeCodePolicy) &&
|
|
935
|
+
mcpStdioCapsuleNativeCodePolicyDigest(contract.nativeCodePolicy) ===
|
|
936
|
+
issued.nativeCodePolicyDigest &&
|
|
921
937
|
args.length === issued.args.length &&
|
|
922
938
|
args.every((value, index) => value === issued.args[index])
|
|
923
939
|
);
|