chainlesschain 0.165.6 → 0.165.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +2 -1
- package/src/assets/web-panel/.build-hash +1 -1
- package/src/assets/web-panel/assets/{AIOps-vfoDb6HZ.js → AIOps-DxwjrnEA.js} +1 -1
- package/src/assets/web-panel/assets/{ActionButton-Cm5f7p93.js → ActionButton-JdbYowzn.js} +1 -1
- package/src/assets/web-panel/assets/{Analytics-yRuvgWxa.js → Analytics-C06GCahv.js} +3 -3
- package/src/assets/web-panel/assets/{AppLayout-BROsC4h1.js → AppLayout-BUVEEyi1.js} +4 -4
- package/src/assets/web-panel/assets/{Artifacts-CimHApb1.js → Artifacts-D_ORzkJd.js} +1 -1
- package/src/assets/web-panel/assets/{Audit-DI8kr8zI.js → Audit-8o_Y5ejG.js} +1 -1
- package/src/assets/web-panel/assets/{BackgroundAgents-DSLzKy-n.js → BackgroundAgents-DrkHd5VU.js} +1 -1
- package/src/assets/web-panel/assets/{Backup-DkH_n2r1.js → Backup-CKBZCX11.js} +1 -1
- package/src/assets/web-panel/assets/{BaseInput-CKSrUoRA.js → BaseInput-C8HkI399.js} +1 -1
- package/src/assets/web-panel/assets/{Chat-ClwfMXUS.js → Chat-zb2H2Wux.js} +6 -6
- package/src/assets/web-panel/assets/{ChatBubbleRenderer-CjMaqfL7.js → ChatBubbleRenderer-BDcNpU_F.js} +1 -1
- package/src/assets/web-panel/assets/{Checkbox-BYTehcQG.js → Checkbox-MwPtQmNz.js} +1 -1
- package/src/assets/web-panel/assets/{Codegen-B8AMcp6J.js → Codegen-3p5QCkFK.js} +1 -1
- package/src/assets/web-panel/assets/{Col-BNSogPo3.js → Col-BPLlhn52.js} +1 -1
- package/src/assets/web-panel/assets/{Community-JxPvGb8O.js → Community-C-3dBNOM.js} +1 -1
- package/src/assets/web-panel/assets/{Compact-DF7bFAgE.js → Compact-BUcy-F_b.js} +1 -1
- package/src/assets/web-panel/assets/{Compliance-Bvhqnwrq.js → Compliance-CI8NxT8m.js} +1 -1
- package/src/assets/web-panel/assets/{Cowork-BDS0CJvM.js → Cowork-DaDEBS-Q.js} +3 -3
- package/src/assets/web-panel/assets/{Cron-DqvRfxoQ.js → Cron-BSv9HXu5.js} +2 -2
- package/src/assets/web-panel/assets/{Crosschain-CDcIvnqS.js → Crosschain-yuiKlJmv.js} +1 -1
- package/src/assets/web-panel/assets/{DID-D7jaUxU3.js → DID-D6LfldB0.js} +2 -2
- package/src/assets/web-panel/assets/{Dashboard-B5A18wEV.js → Dashboard-CBhLqG61.js} +2 -2
- package/src/assets/web-panel/assets/{Dropdown-_6IVkGag.js → Dropdown-BG8pIl3O.js} +1 -1
- package/src/assets/web-panel/assets/{EmailListRenderer-C9PeiKCG.js → EmailListRenderer-CY92VIE6.js} +1 -1
- package/src/assets/web-panel/assets/{FamilyGuardDashboard-DXjuEHrX.js → FamilyGuardDashboard-1Q_ehauM.js} +1 -1
- package/src/assets/web-panel/assets/{Federation-B4IAR9zv.js → Federation-D3XirJIU.js} +1 -1
- package/src/assets/web-panel/assets/{FormItemContext-CxQeSNx1.js → FormItemContext-DfI9UuGZ.js} +1 -1
- package/src/assets/web-panel/assets/{GenericCardRenderer-B8GS-Y3N.js → GenericCardRenderer-IeCZk2m7.js} +1 -1
- package/src/assets/web-panel/assets/{Git-BS8SouUX.js → Git-DzK4IJAZ.js} +2 -2
- package/src/assets/web-panel/assets/{Governance-Dq9aYb5Q.js → Governance-BHf5KfmQ.js} +1 -1
- package/src/assets/web-panel/assets/{Inference-BrCEe9AJ.js → Inference-BmaxSALS.js} +1 -1
- package/src/assets/web-panel/assets/{KnowledgeGraph-DybOpiY2.js → KnowledgeGraph-BgePFLA9.js} +1 -1
- package/src/assets/web-panel/assets/{Logs-DBpzbRfx.js → Logs-nqgxpOc4.js} +2 -2
- package/src/assets/web-panel/assets/MarkdownRenderer-xkX6izBt.js +1 -0
- package/src/assets/web-panel/assets/{Marketplace-Cc7CCSCu.js → Marketplace-Bu8to-86.js} +1 -1
- package/src/assets/web-panel/assets/McpTools-BFWTzEk0.js +4 -0
- package/src/assets/web-panel/assets/{Memory-KCqlp9ol.js → Memory-B026G2GA.js} +2 -2
- package/src/assets/web-panel/assets/{MobileBridge-Bys84ySN.js → MobileBridge-CM2Eeg6I.js} +1 -1
- package/src/assets/web-panel/assets/{MobileProjects-B-6qU-je.js → MobileProjects-B79Ob3hy.js} +1 -1
- package/src/assets/web-panel/assets/{Mtc-BgY-fvCq.js → Mtc-CxevSZuV.js} +4 -4
- package/src/assets/web-panel/assets/{MtcAudit-B8pCmqQJ.js → MtcAudit-X396cPEX.js} +4 -4
- package/src/assets/web-panel/assets/{Multisig-BbOmYutA.js → Multisig-HWIB_aZ1.js} +3 -3
- package/src/assets/web-panel/assets/{NLProgramming-BocLQz_M.js → NLProgramming-gnYD1Xwy.js} +1 -1
- package/src/assets/web-panel/assets/{Notes-CSwbxEop.js → Notes-Dn2Gf_J1.js} +3 -3
- package/src/assets/web-panel/assets/{NotificationSettings-CqFNSYG3.js → NotificationSettings-BRKBQ5rq.js} +1 -1
- package/src/assets/web-panel/assets/{OrderTableRenderer-C6gMfAk7.js → OrderTableRenderer-Dy___Cgo.js} +1 -1
- package/src/assets/web-panel/assets/{Organization-QrDXVdMJ.js → Organization-cqfccxnt.js} +4 -4
- package/src/assets/web-panel/assets/{Overflow-Cxntotjt.js → Overflow-CSX4FHN3.js} +1 -1
- package/src/assets/web-panel/assets/{P2P-CIdBOwNq.js → P2P-Bwa4aghp.js} +2 -2
- package/src/assets/web-panel/assets/{PdhVaultBrowser-CT5aP8rD.js → PdhVaultBrowser-Dhszyjig.js} +3 -3
- package/src/assets/web-panel/assets/{Permissions-DH7OM8XU.js → Permissions-CXzTyk6e.js} +4 -4
- package/src/assets/web-panel/assets/{PersonalDataHub-CnJqbWce.js → PersonalDataHub-CFCAAJbb.js} +4 -4
- package/src/assets/web-panel/assets/{Pipeline-5C3HG1kH.js → Pipeline-BPJbx6xt.js} +1 -1
- package/src/assets/web-panel/assets/{Privacy-B4yIp7xK.js → Privacy-BbiXvEHz.js} +1 -1
- package/src/assets/web-panel/assets/{ProjectInit-BMvt-2M0.js → ProjectInit-B-s7-wSD.js} +2 -2
- package/src/assets/web-panel/assets/{ProjectSettings-CvSTEMRG.js → ProjectSettings-Dyug206y.js} +2 -2
- package/src/assets/web-panel/assets/{Projects-BKBJxEek.js → Projects-BtAI5Fwo.js} +1 -1
- package/src/assets/web-panel/assets/{Providers-BdwUqFWN.js → Providers-JpIPmz6N.js} +1 -1
- package/src/assets/web-panel/assets/{QrScannerModal-DtnCKlTm.js → QrScannerModal-CZdoN64A.js} +1 -1
- package/src/assets/web-panel/assets/{QuickAsk-sTbgwyNr.js → QuickAsk-BwvnoRIT.js} +1 -1
- package/src/assets/web-panel/assets/{Recommend-Bzr14gSb.js → Recommend-ASMlMQrD.js} +1 -1
- package/src/assets/web-panel/assets/{RemoteSession-C_TF5IaJ.js → RemoteSession-BiJzKFKi.js} +3 -3
- package/src/assets/web-panel/assets/{Reputation-GCAM4H2K.js → Reputation-C80TWLIO.js} +1 -1
- package/src/assets/web-panel/assets/{Row-Dir0r6OQ.js → Row-C1VNmjmX.js} +1 -1
- package/src/assets/web-panel/assets/{RssFeed-CpGnlecX.js → RssFeed-T2MyGUlG.js} +3 -3
- package/src/assets/web-panel/assets/{Search-DMMamC6u.js → Search-DXM6O83L.js} +1 -1
- package/src/assets/web-panel/assets/{Security-yqay5yG-.js → Security-nv69kU7W.js} +3 -3
- package/src/assets/web-panel/assets/{Services-C5i2wh9G.js → Services-AyHWe7Qj.js} +2 -2
- package/src/assets/web-panel/assets/{Skeleton-vUqk35yj.js → Skeleton-D6PWLxFI.js} +1 -1
- package/src/assets/web-panel/assets/{Skills-DzcSkhot.js → Skills-BqVeOmNQ.js} +1 -1
- package/src/assets/web-panel/assets/{Sla-CB6qbYNr.js → Sla-BegIPl9M.js} +1 -1
- package/src/assets/web-panel/assets/{SpeechSettings-BHUI7cox.js → SpeechSettings-CKJn3xk-.js} +1 -1
- package/src/assets/web-panel/assets/{SyncSettings-D-RrsxTJ.js → SyncSettings-Th4cnAMl.js} +2 -2
- package/src/assets/web-panel/assets/{Tasks-DFxdOJiq.js → Tasks-BuKfBroh.js} +1 -1
- package/src/assets/web-panel/assets/{Templates-SKAob_Aq.js → Templates-CMuWN8Ac.js} +1 -1
- package/src/assets/web-panel/assets/{Tenant-BPfsfHQ-.js → Tenant-C4je8E8Q.js} +1 -1
- package/src/assets/web-panel/assets/{Terminal-8iw7FpUf.js → Terminal-agJESOcO.js} +2 -2
- package/src/assets/web-panel/assets/{TimelineRenderer-C4Gjj5Su.js → TimelineRenderer-D2qDuVZ_.js} +1 -1
- package/src/assets/web-panel/assets/{Tokens-COYhN18e.js → Tokens-Cvmn99rH.js} +1 -1
- package/src/assets/web-panel/assets/{Trigger-B-JzXcf6.js → Trigger-D_QYIHH8.js} +1 -1
- package/src/assets/web-panel/assets/{Trust-BJ5bg0nI.js → Trust-BmzvQLnX.js} +1 -1
- package/src/assets/web-panel/assets/{UkeySign-BG_ByRMf.js → UkeySign-D_LopHwH.js} +1 -1
- package/src/assets/web-panel/assets/{VideoEditing-B3kOnSiU.js → VideoEditing-DoWl3PhQ.js} +1 -1
- package/src/assets/web-panel/assets/{Wallet-CcoGFnK7.js → Wallet-5NYcAkxe.js} +4 -4
- package/src/assets/web-panel/assets/{WebAuthn-DdT8vRC_.js → WebAuthn-B-_Q3ewp.js} +4 -4
- package/src/assets/web-panel/assets/{WorkflowEditor-CZu3M9N8.js → WorkflowEditor-CHET-gi2.js} +1 -1
- package/src/assets/web-panel/assets/{chat-DxJt3Qba.js → chat-C6nvb-n6.js} +1 -1
- package/src/assets/web-panel/assets/{colors-DdYfwJaB.js → colors-CTDE9LTX.js} +1 -1
- package/src/assets/web-panel/assets/{compact-item-BG3SUzwj.js → compact-item-KnemdF8D.js} +1 -1
- package/src/assets/web-panel/assets/{createContext-By51z2-1.js → createContext-DUJR3Uek.js} +1 -1
- package/src/assets/web-panel/assets/devWarning-1DqLH1KX.js +1 -0
- package/src/assets/web-panel/assets/{hasIn-B0apZ3KJ.js → hasIn-CFNTaby8.js} +1 -1
- package/src/assets/web-panel/assets/{index-ZxtP8jG7.js → index-65HO56Bv.js} +1 -1
- package/src/assets/web-panel/assets/{index-Da_WBdLX.js → index-B8RtbSkV.js} +1 -1
- package/src/assets/web-panel/assets/{index-BBPf3TpM.js → index-BHqSqnW7.js} +1 -1
- package/src/assets/web-panel/assets/{index-BP94CrBV.js → index-BTetEUWd.js} +1 -1
- package/src/assets/web-panel/assets/{index-p1_8SbI1.js → index-BXWg1crv.js} +1 -1
- package/src/assets/web-panel/assets/{index-BWF8_mkx.js → index-BdiC4LOs.js} +1 -1
- package/src/assets/web-panel/assets/index-BhJtcPWo.js +1 -0
- package/src/assets/web-panel/assets/{index-SQqOlR3d.js → index-BuizJo2E.js} +1 -1
- package/src/assets/web-panel/assets/{index-CpjZhgNd.js → index-BwZM1rod.js} +1 -1
- package/src/assets/web-panel/assets/{index-CzmQElZy.js → index-C96N9xeL.js} +1 -1
- package/src/assets/web-panel/assets/{index-bP_VlDb4.js → index-CHfUdnJH.js} +1 -1
- package/src/assets/web-panel/assets/{index-ZFbps9po.js → index-CKvBF3Ov.js} +1 -1
- package/src/assets/web-panel/assets/{index-DaIz0NAW.js → index-CSD9_I2_.js} +1 -1
- package/src/assets/web-panel/assets/{index-BewKYrX9.js → index-Cc7dbl_p.js} +1 -1
- package/src/assets/web-panel/assets/{index-CNIxXMmN.js → index-CcF0D7UZ.js} +1 -1
- package/src/assets/web-panel/assets/{index-Q2SCX-MI.js → index-Ceu92a8D.js} +1 -1
- package/src/assets/web-panel/assets/{index-DE76D-kA.js → index-CnLpuTAU.js} +1 -1
- package/src/assets/web-panel/assets/index-Cq8MWPDT.js +1 -0
- package/src/assets/web-panel/assets/{index-BkvcEo4p.js → index-CquKnw5r.js} +1 -1
- package/src/assets/web-panel/assets/{index-Cp6e-hGw.js → index-CyEtHvEE.js} +1 -1
- package/src/assets/web-panel/assets/{index-BLIbGOLX.js → index-DF-fcTvQ.js} +1 -1
- package/src/assets/web-panel/assets/{index-DCn8IcxV.js → index-DF1Y0Vuw.js} +1 -1
- package/src/assets/web-panel/assets/{index-Bissefox.js → index-DJyPeBBk.js} +3 -3
- package/src/assets/web-panel/assets/{index-BZ_0qm4g.js → index-DP-PbO11.js} +1 -1
- package/src/assets/web-panel/assets/{index-kf57LJ93.js → index-DZgCRMxk.js} +1 -1
- package/src/assets/web-panel/assets/{index-BYINoRLK.js → index-DfJzS3Qa.js} +1 -1
- package/src/assets/web-panel/assets/{index-3jWkIu5j.js → index-DqPpJkte.js} +1 -1
- package/src/assets/web-panel/assets/{index-B5rLiEo0.js → index-DzReoLyh.js} +1 -1
- package/src/assets/web-panel/assets/{index-CW93tZDv.js → index-JGTiDeOW.js} +1 -1
- package/src/assets/web-panel/assets/{index-C1C9i79A.js → index-NUR0If7O.js} +1 -1
- package/src/assets/web-panel/assets/{index-CAhHirVH.js → index-NtAcnJYW.js} +1 -1
- package/src/assets/web-panel/assets/{index-RD6O-0C6.js → index-OmuhFYKE.js} +1 -1
- package/src/assets/web-panel/assets/{index-oxRrmFO9.js → index-Wlc7o_C8.js} +1 -1
- package/src/assets/web-panel/assets/{index-B3rWuPMW.js → index-bxTPfdri.js} +1 -1
- package/src/assets/web-panel/assets/{index-BIrJfqO4.js → index-k6uorYmf.js} +1 -1
- package/src/assets/web-panel/assets/{index-BfWYsB9t.js → index-ox6mIwE2.js} +1 -1
- package/src/assets/web-panel/assets/{index-Bos5_aMV.js → index-rVDD7AAR.js} +1 -1
- package/src/assets/web-panel/assets/{index-CWEuZWCK.js → index-u0h6QqVV.js} +1 -1
- package/src/assets/web-panel/assets/{index-Dz49VOyQ.js → index-v-mtxmWH.js} +1 -1
- package/src/assets/web-panel/assets/{initDefaultProps-BWJktcg8.js → initDefaultProps-qEt8dI-t.js} +1 -1
- package/src/assets/web-panel/assets/{motion-BEXBzUFV.js → motion-DetKPWy-.js} +1 -1
- package/src/assets/web-panel/assets/{move-BUKwn2sx.js → move-BeNYeDV2.js} +1 -1
- package/src/assets/web-panel/assets/{mtc-parser-FTI7btqQ.js → mtc-parser-C0q0MK_T.js} +1 -1
- package/src/assets/web-panel/assets/{omit-CsLtSPH3.js → omit-DLKkrKpE.js} +1 -1
- package/src/assets/web-panel/assets/{pickAttrs-DWZxsT_S.js → pickAttrs-Bw1tWsY8.js} +1 -1
- package/src/assets/web-panel/assets/{placementArrow-Bcfs43CX.js → placementArrow-DeesBLbd.js} +1 -1
- package/src/assets/web-panel/assets/{responsiveObserve-a9bcC1p-.js → responsiveObserve-bqLhxHhn.js} +1 -1
- package/src/assets/web-panel/assets/{slide-BDTzgOXN.js → slide-DGCXRoQm.js} +1 -1
- package/src/assets/web-panel/assets/{statusUtils-Iq1j_HUg.js → statusUtils-H-re3Egl.js} +1 -1
- package/src/assets/web-panel/assets/{styleChecker-DAZEYxeh.js → styleChecker-BXg4qGX_.js} +1 -1
- package/src/assets/web-panel/assets/{useFlexGapSupport-DVkjyDrh.js → useFlexGapSupport-CKLb2KA6.js} +1 -1
- package/src/assets/web-panel/assets/{useFs-wXTpNTHt.js → useFs-CTAhHeyw.js} +1 -1
- package/src/assets/web-panel/assets/{usePersonalDataHub-CtQM_aZr.js → usePersonalDataHub-BQ4pDh5g.js} +1 -1
- package/src/assets/web-panel/assets/{vnode-hjEoG85v.js → vnode-jrSK6j4j.js} +1 -1
- package/src/assets/web-panel/assets/{zoom-BHVpyKg_.js → zoom-fOQE24pw.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 +91 -60
- package/src/commands/git.js +39 -0
- package/src/commands/plugin.js +17 -2
- package/src/commands/session-group.js +131 -0
- package/src/commands/session-location.js +48 -0
- package/src/commands/session.js +11 -0
- package/src/commands/sync-providers.js +4 -1
- package/src/commands/update.js +1 -0
- package/src/data/changelog.json +40 -1
- package/src/gateways/ws/mcp-topic-handlers.js +167 -0
- package/src/gateways/ws/ws-agent-handler.js +20 -12
- package/src/harness/jsonl-session-store.js +279 -37
- package/src/harness/mcp-client.js +701 -91
- package/src/harness/session-index.js +15 -4
- package/src/lib/agent-network.js +166 -29
- package/src/lib/agent-router.js +62 -6
- package/src/lib/agent-sandbox.js +7 -1
- package/src/lib/agent-worktree.js +8 -1
- package/src/lib/auto-mode-safety-classifier.js +3 -17
- package/src/lib/background-agent-keeper-protocol.js +26 -6
- package/src/lib/background-agent-supervisor.js +17 -1
- package/src/lib/browser-evidence.js +1405 -0
- package/src/lib/chrome-connector.js +465 -79
- package/src/lib/claude-code-bridge.js +159 -54
- package/src/lib/claude-project-auto-memory.js +481 -0
- package/src/lib/claude-project-storage-layout.js +104 -0
- package/src/lib/cloud/cloud-client.js +40 -0
- package/src/lib/cowork-workflow.js +69 -6
- package/src/lib/execution-location-local-supervisor.mjs +226 -0
- package/src/lib/execution-location-runner-lifecycle.js +991 -0
- package/src/lib/execution-location-target-preflight.js +333 -0
- package/src/lib/execution-location-target.js +783 -13
- package/src/lib/external-agent-adapters.js +269 -0
- package/src/lib/file-lines.js +6 -1
- package/src/lib/gateway-client-resilience.js +571 -0
- package/src/lib/gitlab-mr-worktree.js +176 -0
- package/src/lib/hooks-v2-producers.js +119 -15
- package/src/lib/hooks-v2-workspace-context.js +44 -1
- package/src/lib/host-resource-budget.js +295 -0
- package/src/lib/ide-context.js +15 -2
- package/src/lib/ipfs-storage.js +127 -25
- package/src/lib/llm-config-defaults.js +43 -0
- package/src/lib/mcp-lifecycle-authority.js +887 -0
- package/src/lib/mcp-oauth.js +101 -20
- package/src/lib/mcp-tls.js +103 -3
- package/src/lib/orchestrator.js +47 -0
- package/src/lib/paths.js +260 -32
- package/src/lib/permission-request.js +48 -0
- package/src/lib/plugin-runtime/bin.js +3 -1
- package/src/lib/plugin-runtime/hooks.js +3 -1
- package/src/lib/plugin-runtime/install.js +117 -1
- package/src/lib/plugin-runtime/lsp.js +4 -4
- package/src/lib/plugin-runtime/marketplace-archive-source.js +40 -5
- package/src/lib/plugin-runtime/marketplace-catalog.js +13 -2
- package/src/lib/plugin-runtime/marketplace-command-descriptor.js +343 -0
- package/src/lib/plugin-runtime/marketplace-command-source.js +580 -0
- package/src/lib/plugin-runtime/marketplace-source-adapter.js +33 -8
- package/src/lib/plugin-runtime/mcp.js +3 -1
- package/src/lib/plugin-runtime/monitors.js +3 -1
- package/src/lib/plugin-runtime/remote-source.js +151 -6
- package/src/lib/plugin-runtime/settings.js +3 -1
- package/src/lib/plugin-runtime/trust.js +274 -13
- package/src/lib/process-execution-broker/credential-agent.js +210 -41
- package/src/lib/process-execution-broker/index.js +15 -1
- package/src/lib/process-execution-broker/linux-cgroup-v2.js +391 -0
- package/src/lib/process-execution-broker/platform-sandbox.js +50 -28
- package/src/lib/project-mcp-trust.js +165 -10
- package/src/lib/resume-session.js +9 -6
- package/src/lib/session-core-singletons.js +26 -11
- package/src/lib/session-host-lease.js +52 -16
- package/src/lib/session-lifecycle-scope.js +153 -0
- package/src/lib/session-message-fabric.js +168 -21
- package/src/lib/session-projection.js +244 -4
- package/src/lib/session-store-guard.js +3 -1
- package/src/lib/session-workbench-store.js +466 -0
- package/src/lib/shell-approval.js +7 -5
- package/src/lib/spellcheck.js +211 -0
- package/src/lib/sub-agent-context.js +9 -9
- package/src/lib/sync-cli-db.js +485 -129
- package/src/lib/sync-credentials.js +31 -20
- package/src/lib/web-fetch.js +112 -40
- package/src/lib/web-search.js +127 -61
- package/src/lib/workflow-engine.js +42 -11
- package/src/lib/workspace-trust.js +793 -0
- package/src/repl/agent-repl.js +124 -5
- package/src/repl/permission-prompt.js +10 -12
- package/src/runtime/agent-core.js +311 -18
- package/src/runtime/agent-runtime.js +12 -1
- package/src/runtime/bootstrap.js +37 -13
- package/src/runtime/coding-agent-contract-shared.cjs +11 -4
- package/src/runtime/headless-runner.js +234 -36
- package/src/runtime/headless-stream.js +181 -6
- package/src/runtime/mcp-config.js +298 -8
- package/src/runtime/policies/agent-policy.js +8 -0
- package/src/runtime/status-diagnostics-lite.js +9 -1
- package/src/assets/web-panel/assets/MarkdownRenderer-CFyt8iUd.js +0 -1
- package/src/assets/web-panel/assets/McpTools-DFaIhOfu.js +0 -7
- package/src/assets/web-panel/assets/devWarning-DorRZcI9.js +0 -1
- package/src/assets/web-panel/assets/index-BETiL0a3.js +0 -1
- package/src/assets/web-panel/assets/index-BhAVHuW8.js +0 -1
|
@@ -27,11 +27,20 @@
|
|
|
27
27
|
* makes.
|
|
28
28
|
*/
|
|
29
29
|
import fs from "fs";
|
|
30
|
+
import crypto from "crypto";
|
|
30
31
|
import os from "os";
|
|
31
32
|
import path from "path";
|
|
32
33
|
import http from "http";
|
|
33
34
|
import executionBroker from "./process-execution-broker/index.js";
|
|
34
35
|
import { redactSecrets } from "./secret-scan.js";
|
|
36
|
+
import {
|
|
37
|
+
authorizeBrowserAction,
|
|
38
|
+
authorizeBrowserReplay,
|
|
39
|
+
browserEvidenceDigest,
|
|
40
|
+
createBrowserEvidenceEnvelope,
|
|
41
|
+
describeBrowserAction,
|
|
42
|
+
normalizeBrowserEvidenceBinding,
|
|
43
|
+
} from "./browser-evidence.js";
|
|
35
44
|
|
|
36
45
|
export const DEFAULT_CDP_PORT = 9222;
|
|
37
46
|
|
|
@@ -138,6 +147,44 @@ function redactBrowserDomUrl(value, cap = 1000) {
|
|
|
138
147
|
return redactBrowserText(`${prefix}${query}`, cap);
|
|
139
148
|
}
|
|
140
149
|
|
|
150
|
+
function isSensitiveInputTag(tag) {
|
|
151
|
+
return (
|
|
152
|
+
/\btype\s*=\s*["']?password\b/i.test(tag) ||
|
|
153
|
+
/\b(?:name|id)\s*=\s*["'][^"']*(?:password|passwd|token|secret|api[-_]?key)[^"']*["']/i.test(
|
|
154
|
+
tag,
|
|
155
|
+
)
|
|
156
|
+
);
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
export function browserDomRedactionMetadata(value) {
|
|
160
|
+
const html = String(value || "");
|
|
161
|
+
const inputTags = html.match(/<input\b[^>]*>/gi) || [];
|
|
162
|
+
const sensitiveInputValues = inputTags.filter(
|
|
163
|
+
(tag) =>
|
|
164
|
+
isSensitiveInputTag(tag) && /\bvalue\s*=\s*["'][^"']*["']/i.test(tag),
|
|
165
|
+
).length;
|
|
166
|
+
const sensitiveTextareaValues = (
|
|
167
|
+
html.match(
|
|
168
|
+
/<textarea\b[^>]*(?:name|id)\s*=\s*["'][^"']*(?:password|passwd|token|secret|api[-_]?key)[^"']*["'][^>]*>[\s\S]*?<\/textarea>/gi,
|
|
169
|
+
) || []
|
|
170
|
+
).length;
|
|
171
|
+
let urlQueryValues = 0;
|
|
172
|
+
for (const match of html.matchAll(
|
|
173
|
+
/\b(?:href|src|action)\s*=\s*["']([^"']+)["']/gi,
|
|
174
|
+
)) {
|
|
175
|
+
const query = String(match[1]).split("#", 1)[0].split("?", 2)[1] || "";
|
|
176
|
+
urlQueryValues += query
|
|
177
|
+
.split(/&(?:amp;)?/iu)
|
|
178
|
+
.filter((part) => part.trim() !== "").length;
|
|
179
|
+
}
|
|
180
|
+
return Object.freeze({
|
|
181
|
+
applied: true,
|
|
182
|
+
sensitiveFieldValues: sensitiveInputValues + sensitiveTextareaValues,
|
|
183
|
+
urlQueryValues,
|
|
184
|
+
secretPatterns: redactSecrets(html) === html ? 0 : 1,
|
|
185
|
+
});
|
|
186
|
+
}
|
|
187
|
+
|
|
141
188
|
/**
|
|
142
189
|
* Redact secret-shaped text, sensitive form values, and DOM URL query values
|
|
143
190
|
* from the bounded snapshot before it crosses the tool boundary.
|
|
@@ -145,12 +192,7 @@ function redactBrowserDomUrl(value, cap = 1000) {
|
|
|
145
192
|
export function redactBrowserDom(value, cap = DEFAULT_DOM_CAP) {
|
|
146
193
|
let html = redactSecrets(String(value || ""));
|
|
147
194
|
html = html.replace(/<input\b[^>]*>/gi, (tag) => {
|
|
148
|
-
|
|
149
|
-
/\btype\s*=\s*["']?password\b/i.test(tag) ||
|
|
150
|
-
/\b(?:name|id)\s*=\s*["'][^"']*(?:password|passwd|token|secret|api[-_]?key)[^"']*["']/i.test(
|
|
151
|
-
tag,
|
|
152
|
-
);
|
|
153
|
-
if (!sensitive) return tag;
|
|
195
|
+
if (!isSensitiveInputTag(tag)) return tag;
|
|
154
196
|
return tag.replace(/\bvalue\s*=\s*(["'])[^"']*\1/i, 'value="[REDACTED]"');
|
|
155
197
|
});
|
|
156
198
|
html = html.replace(
|
|
@@ -168,10 +210,11 @@ export function redactBrowserDom(value, cap = DEFAULT_DOM_CAP) {
|
|
|
168
210
|
function redactScreenshotFailure(error, generatedPath, cap = 300) {
|
|
169
211
|
let message = String(error && error.message ? error.message : error);
|
|
170
212
|
if (generatedPath) {
|
|
171
|
-
message = message
|
|
172
|
-
.split(String(generatedPath))
|
|
173
|
-
.join("[SCREENSHOT_PATH]");
|
|
213
|
+
message = message.split(String(generatedPath)).join("[SCREENSHOT_PATH]");
|
|
174
214
|
}
|
|
215
|
+
message = message.replace(/https?:\/\/[^\s"'<>]+/giu, (url) =>
|
|
216
|
+
redactBrowserUrl(url, cap),
|
|
217
|
+
);
|
|
175
218
|
return redactBrowserText(message.split("\n")[0], cap);
|
|
176
219
|
}
|
|
177
220
|
|
|
@@ -184,6 +227,15 @@ function cleanupFailedScreenshot(generatedPath, deps) {
|
|
|
184
227
|
}
|
|
185
228
|
}
|
|
186
229
|
|
|
230
|
+
function digestGeneratedFile(generatedPath, deps) {
|
|
231
|
+
if (!generatedPath) return null;
|
|
232
|
+
try {
|
|
233
|
+
return browserEvidenceDigest(deps.fs.readFileSync(generatedPath));
|
|
234
|
+
} catch {
|
|
235
|
+
return null;
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
|
|
187
239
|
const _deps = {
|
|
188
240
|
fs,
|
|
189
241
|
spawn: executionBroker.spawn.bind(executionBroker),
|
|
@@ -427,9 +479,13 @@ export async function captureState({
|
|
|
427
479
|
});
|
|
428
480
|
}
|
|
429
481
|
};
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
482
|
+
const canObserve =
|
|
483
|
+
typeof page.on === "function" && typeof page.off === "function";
|
|
484
|
+
if (canObserve) {
|
|
485
|
+
page.on("console", onConsole);
|
|
486
|
+
page.on("requestfailed", onRequestFailed);
|
|
487
|
+
page.on("response", onResponse);
|
|
488
|
+
}
|
|
433
489
|
try {
|
|
434
490
|
if (reload) {
|
|
435
491
|
await page
|
|
@@ -440,6 +496,7 @@ export async function captureState({
|
|
|
440
496
|
|
|
441
497
|
const state = {
|
|
442
498
|
ok: true,
|
|
499
|
+
observationCaptureAvailable: canObserve,
|
|
443
500
|
port,
|
|
444
501
|
tab: index,
|
|
445
502
|
url: redactBrowserUrl(page.url()),
|
|
@@ -452,8 +509,16 @@ export async function captureState({
|
|
|
452
509
|
network: networkEntries,
|
|
453
510
|
};
|
|
454
511
|
if (includeDom) {
|
|
455
|
-
|
|
512
|
+
let htmlCaptureSucceeded = true;
|
|
513
|
+
const html = await page.content().catch(() => {
|
|
514
|
+
htmlCaptureSucceeded = false;
|
|
515
|
+
return "";
|
|
516
|
+
});
|
|
456
517
|
state.html = redactBrowserDom(html, domCap);
|
|
518
|
+
state.htmlCaptureSucceeded = htmlCaptureSucceeded;
|
|
519
|
+
state.htmlRedaction = browserDomRedactionMetadata(html);
|
|
520
|
+
state.htmlSourceChars = html.length;
|
|
521
|
+
state.htmlDigest = browserEvidenceDigest(state.html);
|
|
457
522
|
state.htmlTruncated = html.length > domCap;
|
|
458
523
|
}
|
|
459
524
|
if (screenshotPath) {
|
|
@@ -468,13 +533,16 @@ export async function captureState({
|
|
|
468
533
|
if (!state.screenshotError) {
|
|
469
534
|
state.screenshotPath = screenshotPath;
|
|
470
535
|
state.screenshotRef = path.basename(screenshotPath);
|
|
536
|
+
state.screenshotSha256 = digestGeneratedFile(screenshotPath, deps);
|
|
471
537
|
}
|
|
472
538
|
}
|
|
473
539
|
return state;
|
|
474
540
|
} finally {
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
541
|
+
if (canObserve) {
|
|
542
|
+
page.off("console", onConsole);
|
|
543
|
+
page.off("requestfailed", onRequestFailed);
|
|
544
|
+
page.off("response", onResponse);
|
|
545
|
+
}
|
|
478
546
|
}
|
|
479
547
|
} finally {
|
|
480
548
|
// connectOverCDP close() disconnects the client; the browser lives on.
|
|
@@ -501,6 +569,8 @@ export const SUPPORTED_BROWSER_ACTIONS = Object.freeze([
|
|
|
501
569
|
"waitForSelector",
|
|
502
570
|
"screenshot",
|
|
503
571
|
"assertText",
|
|
572
|
+
"upload",
|
|
573
|
+
"download",
|
|
504
574
|
]);
|
|
505
575
|
export const MAX_BROWSER_ACTIONS = 30;
|
|
506
576
|
export const DEFAULT_ACTION_TIMEOUT_MS = 10000;
|
|
@@ -614,12 +684,15 @@ export function normalizeBrowserActions(actions, { deps = _deps } = {}) {
|
|
|
614
684
|
`navigate url must be http(s):// (got ${parsed.protocol})`,
|
|
615
685
|
);
|
|
616
686
|
}
|
|
687
|
+
if (parsed.username || parsed.password) {
|
|
688
|
+
throw new Error("navigate url must not contain embedded credentials");
|
|
689
|
+
}
|
|
617
690
|
return { type, url: String(raw.url) };
|
|
618
691
|
}
|
|
619
692
|
case "waitForSelector": {
|
|
620
693
|
const t = Number(raw.timeoutMs ?? raw.timeout_ms ?? 5000);
|
|
621
694
|
const timeoutMs = Math.min(
|
|
622
|
-
Math.max(Number.isFinite(t) ? t : 5000, 1),
|
|
695
|
+
Math.max(Number.isFinite(t) ? Math.trunc(t) : 5000, 1),
|
|
623
696
|
MAX_ACTION_TIMEOUT_MS,
|
|
624
697
|
);
|
|
625
698
|
return {
|
|
@@ -643,7 +716,7 @@ export function normalizeBrowserActions(actions, { deps = _deps } = {}) {
|
|
|
643
716
|
type,
|
|
644
717
|
screenshotPath: path.join(
|
|
645
718
|
deps.tmpdir(),
|
|
646
|
-
`cc-browser-act-${
|
|
719
|
+
`cc-browser-act-${crypto.randomBytes(16).toString("hex")}-${i}.png`,
|
|
647
720
|
),
|
|
648
721
|
};
|
|
649
722
|
}
|
|
@@ -657,6 +730,41 @@ export function normalizeBrowserActions(actions, { deps = _deps } = {}) {
|
|
|
657
730
|
expected: raw.expected,
|
|
658
731
|
};
|
|
659
732
|
}
|
|
733
|
+
case "upload": {
|
|
734
|
+
for (const key of ["path", "file", "files", "inputFiles"]) {
|
|
735
|
+
if (raw[key] != null) {
|
|
736
|
+
throw new Error(
|
|
737
|
+
`upload accepts only a managed session artifact_id; remove "${key}"`,
|
|
738
|
+
);
|
|
739
|
+
}
|
|
740
|
+
}
|
|
741
|
+
const artifactId = String(raw.artifactId ?? raw.artifact_id ?? "");
|
|
742
|
+
if (!/^art_[A-Za-z0-9_]+$/u.test(artifactId)) {
|
|
743
|
+
throw new Error("upload requires a managed artifact_id");
|
|
744
|
+
}
|
|
745
|
+
return {
|
|
746
|
+
type,
|
|
747
|
+
selector: requireSelector(raw, "upload"),
|
|
748
|
+
artifactId,
|
|
749
|
+
};
|
|
750
|
+
}
|
|
751
|
+
case "download": {
|
|
752
|
+
for (const key of ["path", "file", "output", "downloadPath"]) {
|
|
753
|
+
if (raw[key] != null) {
|
|
754
|
+
throw new Error(
|
|
755
|
+
`download path is generated internally — remove "${key}"`,
|
|
756
|
+
);
|
|
757
|
+
}
|
|
758
|
+
}
|
|
759
|
+
return {
|
|
760
|
+
type,
|
|
761
|
+
selector: requireSelector(raw, "download"),
|
|
762
|
+
downloadPath: path.join(
|
|
763
|
+
deps.tmpdir(),
|
|
764
|
+
`cc-browser-download-${crypto.randomBytes(16).toString("hex")}-${i}.bin`,
|
|
765
|
+
),
|
|
766
|
+
};
|
|
767
|
+
}
|
|
660
768
|
/* c8 ignore next 2 -- unreachable: type already validated above */
|
|
661
769
|
default:
|
|
662
770
|
throw new Error(`unsupported action type "${type}"`);
|
|
@@ -700,29 +808,34 @@ function connectorProfileOwnsPort(port, deps) {
|
|
|
700
808
|
}
|
|
701
809
|
}
|
|
702
810
|
|
|
703
|
-
async function runOneAction(
|
|
811
|
+
async function runOneAction(
|
|
812
|
+
page,
|
|
813
|
+
act,
|
|
814
|
+
timeoutMs,
|
|
815
|
+
{ resolveUploadArtifact = null } = {},
|
|
816
|
+
) {
|
|
704
817
|
switch (act.type) {
|
|
705
818
|
case "click":
|
|
706
819
|
await page.click(act.selector, { timeout: timeoutMs });
|
|
707
|
-
return `clicked ${act.selector}
|
|
820
|
+
return { detail: `clicked ${act.selector}` };
|
|
708
821
|
case "type":
|
|
709
822
|
await page.fill(act.selector, act.text, { timeout: timeoutMs });
|
|
710
|
-
return `typed ${act.text.length} chars into ${act.selector}
|
|
823
|
+
return { detail: `typed ${act.text.length} chars into ${act.selector}` };
|
|
711
824
|
case "press":
|
|
712
825
|
await page.keyboard.press(act.key);
|
|
713
|
-
return `pressed ${act.key}
|
|
826
|
+
return { detail: `pressed ${act.key}` };
|
|
714
827
|
case "navigate":
|
|
715
828
|
await page.goto(act.url, {
|
|
716
829
|
waitUntil: "domcontentloaded",
|
|
717
830
|
timeout: timeoutMs,
|
|
718
831
|
});
|
|
719
|
-
return `navigated to ${act.url}
|
|
832
|
+
return { detail: `navigated to ${act.url}` };
|
|
720
833
|
case "waitForSelector":
|
|
721
834
|
await page.waitForSelector(act.selector, { timeout: act.timeoutMs });
|
|
722
|
-
return `selector appeared: ${act.selector}
|
|
835
|
+
return { detail: `selector appeared: ${act.selector}` };
|
|
723
836
|
case "screenshot":
|
|
724
837
|
await page.screenshot({ path: act.screenshotPath });
|
|
725
|
-
return "screenshot captured";
|
|
838
|
+
return { detail: "screenshot captured" };
|
|
726
839
|
case "assertText": {
|
|
727
840
|
const text = await page.textContent(act.selector, {
|
|
728
841
|
timeout: timeoutMs,
|
|
@@ -732,10 +845,74 @@ async function runOneAction(page, act, timeoutMs) {
|
|
|
732
845
|
}
|
|
733
846
|
if (!text.includes(act.expected)) {
|
|
734
847
|
throw new Error(
|
|
735
|
-
`assertText FAILED:
|
|
848
|
+
`assertText FAILED: expected text was not found in ${act.selector}`,
|
|
736
849
|
);
|
|
737
850
|
}
|
|
738
|
-
return
|
|
851
|
+
return {
|
|
852
|
+
detail: `assertText passed: ${act.selector}`,
|
|
853
|
+
};
|
|
854
|
+
}
|
|
855
|
+
case "upload": {
|
|
856
|
+
if (typeof resolveUploadArtifact !== "function") {
|
|
857
|
+
throw new Error("upload artifact authority is unavailable");
|
|
858
|
+
}
|
|
859
|
+
const resolved = await resolveUploadArtifact(act.artifactId);
|
|
860
|
+
try {
|
|
861
|
+
if (
|
|
862
|
+
!resolved ||
|
|
863
|
+
typeof resolved.path !== "string" ||
|
|
864
|
+
!resolved.metadata?.id ||
|
|
865
|
+
!resolved.metadata?.sha256
|
|
866
|
+
) {
|
|
867
|
+
throw new Error(
|
|
868
|
+
"upload artifact authority returned an invalid record",
|
|
869
|
+
);
|
|
870
|
+
}
|
|
871
|
+
const uploadDigest = String(resolved.metadata.sha256)
|
|
872
|
+
.replace(/^sha256:/u, "")
|
|
873
|
+
.toLowerCase();
|
|
874
|
+
if (
|
|
875
|
+
resolved.metadata.id !== act.artifactId ||
|
|
876
|
+
!/^[a-f0-9]{64}$/u.test(uploadDigest) ||
|
|
877
|
+
!Number.isFinite(Number(resolved.metadata.size)) ||
|
|
878
|
+
Number(resolved.metadata.size) < 0
|
|
879
|
+
) {
|
|
880
|
+
throw new Error(
|
|
881
|
+
"upload artifact authority returned mismatched metadata",
|
|
882
|
+
);
|
|
883
|
+
}
|
|
884
|
+
try {
|
|
885
|
+
await page.setInputFiles(act.selector, resolved.path, {
|
|
886
|
+
timeout: timeoutMs,
|
|
887
|
+
});
|
|
888
|
+
} catch {
|
|
889
|
+
throw new Error("upload failed for the managed session artifact");
|
|
890
|
+
}
|
|
891
|
+
return {
|
|
892
|
+
detail: `uploaded managed artifact ${resolved.metadata.id}`,
|
|
893
|
+
uploadArtifact: {
|
|
894
|
+
id: resolved.metadata.id,
|
|
895
|
+
sha256: `sha256:${uploadDigest}`,
|
|
896
|
+
size: Number(resolved.metadata.size),
|
|
897
|
+
},
|
|
898
|
+
};
|
|
899
|
+
} finally {
|
|
900
|
+
await resolved?.cleanup?.();
|
|
901
|
+
}
|
|
902
|
+
}
|
|
903
|
+
case "download": {
|
|
904
|
+
if (typeof page.waitForEvent !== "function") {
|
|
905
|
+
throw new Error("download capture is unavailable in this browser");
|
|
906
|
+
}
|
|
907
|
+
const [download] = await Promise.all([
|
|
908
|
+
page.waitForEvent("download", { timeout: timeoutMs }),
|
|
909
|
+
page.click(act.selector, { timeout: timeoutMs }),
|
|
910
|
+
]);
|
|
911
|
+
await download.saveAs(act.downloadPath);
|
|
912
|
+
return {
|
|
913
|
+
detail: "download captured",
|
|
914
|
+
suggestedName: redactBrowserText(download.suggestedFilename?.(), 200),
|
|
915
|
+
};
|
|
739
916
|
}
|
|
740
917
|
/* c8 ignore next 2 -- unreachable: normalizeBrowserActions validated type */
|
|
741
918
|
default:
|
|
@@ -760,14 +937,47 @@ export async function performActions(
|
|
|
760
937
|
continueOnError = false,
|
|
761
938
|
sessionId = null,
|
|
762
939
|
actionTimeoutMs = DEFAULT_ACTION_TIMEOUT_MS,
|
|
940
|
+
evidenceBinding = null,
|
|
941
|
+
originGrants = null,
|
|
942
|
+
expectedGrantRevisions = null,
|
|
943
|
+
replaySourceEnvelope = null,
|
|
944
|
+
replayAllowSideEffects = false,
|
|
945
|
+
replayAllowCredentials = false,
|
|
946
|
+
resolveUploadArtifact = null,
|
|
947
|
+
evidenceDomCap = 40000,
|
|
763
948
|
deps = _deps,
|
|
764
949
|
} = {},
|
|
765
950
|
) {
|
|
766
951
|
let normalized;
|
|
767
952
|
let resolvedPort;
|
|
953
|
+
let replay = null;
|
|
768
954
|
try {
|
|
769
955
|
resolvedPort = resolveLoopbackCdpPort(cdpUrl, port);
|
|
770
956
|
normalized = normalizeBrowserActions(actions, { deps });
|
|
957
|
+
if (evidenceBinding) {
|
|
958
|
+
const normalizedBinding =
|
|
959
|
+
normalizeBrowserEvidenceBinding(evidenceBinding);
|
|
960
|
+
if (!sessionId || String(sessionId) !== normalizedBinding.session.id) {
|
|
961
|
+
throw new Error(
|
|
962
|
+
"browser evidence binding is not bound to the active session",
|
|
963
|
+
);
|
|
964
|
+
}
|
|
965
|
+
}
|
|
966
|
+
if (replaySourceEnvelope) {
|
|
967
|
+
replay = authorizeBrowserReplay({
|
|
968
|
+
sourceEnvelope: replaySourceEnvelope,
|
|
969
|
+
binding: evidenceBinding,
|
|
970
|
+
actions: normalized,
|
|
971
|
+
allowSideEffects: replayAllowSideEffects,
|
|
972
|
+
allowCredentials: replayAllowCredentials,
|
|
973
|
+
});
|
|
974
|
+
}
|
|
975
|
+
if (
|
|
976
|
+
(originGrants || expectedGrantRevisions || replaySourceEnvelope) &&
|
|
977
|
+
!evidenceBinding
|
|
978
|
+
) {
|
|
979
|
+
throw new Error("browser evidence authority requires an exact binding");
|
|
980
|
+
}
|
|
771
981
|
} catch (err) {
|
|
772
982
|
return { ok: false, error: err.message };
|
|
773
983
|
}
|
|
@@ -819,63 +1029,239 @@ export async function performActions(
|
|
|
819
1029
|
"(~/.chainlesschain/chrome-profile) — actions run in that browser's REAL session";
|
|
820
1030
|
}
|
|
821
1031
|
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
act.type === "navigate"
|
|
831
|
-
? `navigated to ${redactBrowserUrl(act.url)}`
|
|
832
|
-
: redactBrowserText(detail, 500);
|
|
833
|
-
step.ok = true;
|
|
834
|
-
if (act.screenshotPath) {
|
|
835
|
-
step.screenshotPath = act.screenshotPath;
|
|
836
|
-
step.screenshotRef = path.basename(act.screenshotPath);
|
|
837
|
-
}
|
|
838
|
-
} catch (err) {
|
|
839
|
-
cleanupFailedScreenshot(act.screenshotPath, deps);
|
|
840
|
-
step.detail = redactScreenshotFailure(err, act.screenshotPath, 500);
|
|
1032
|
+
const consoleEntries = [];
|
|
1033
|
+
const networkEntries = [];
|
|
1034
|
+
const onConsole = (message) => {
|
|
1035
|
+
if (consoleEntries.length < 200) {
|
|
1036
|
+
consoleEntries.push({
|
|
1037
|
+
type: redactBrowserText(message.type(), 40),
|
|
1038
|
+
text: redactBrowserText(message.text(), 500),
|
|
1039
|
+
});
|
|
841
1040
|
}
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
1041
|
+
};
|
|
1042
|
+
const onRequestFailed = (request) => {
|
|
1043
|
+
if (networkEntries.length < 200) {
|
|
1044
|
+
networkEntries.push({
|
|
1045
|
+
kind: "failed",
|
|
1046
|
+
url: redactBrowserUrl(request.url(), 300),
|
|
1047
|
+
error: redactBrowserText(request.failure()?.errorText || "", 300),
|
|
1048
|
+
});
|
|
1049
|
+
}
|
|
1050
|
+
};
|
|
1051
|
+
const onResponse = (response) => {
|
|
1052
|
+
if (response.status() >= 400 && networkEntries.length < 200) {
|
|
1053
|
+
networkEntries.push({
|
|
1054
|
+
kind: "http-error",
|
|
1055
|
+
url: redactBrowserUrl(response.url(), 300),
|
|
1056
|
+
status: response.status(),
|
|
1057
|
+
});
|
|
1058
|
+
}
|
|
1059
|
+
};
|
|
1060
|
+
const canObserve =
|
|
1061
|
+
typeof page.on === "function" && typeof page.off === "function";
|
|
1062
|
+
if (canObserve) {
|
|
1063
|
+
page.on("console", onConsole);
|
|
1064
|
+
page.on("requestfailed", onRequestFailed);
|
|
1065
|
+
page.on("response", onResponse);
|
|
1066
|
+
}
|
|
1067
|
+
|
|
1068
|
+
try {
|
|
1069
|
+
let auditError = null;
|
|
1070
|
+
const authorities = [];
|
|
1071
|
+
for (const act of normalized) {
|
|
1072
|
+
const started = Date.now();
|
|
1073
|
+
const step = { ok: false, action: act.type, detail: "", durationMs: 0 };
|
|
1074
|
+
const pageBefore = redactBrowserUrl(page.url());
|
|
1075
|
+
let authority = null;
|
|
1076
|
+
try {
|
|
1077
|
+
if (evidenceBinding) {
|
|
1078
|
+
authority = authorizeBrowserAction({
|
|
1079
|
+
binding: evidenceBinding,
|
|
1080
|
+
grants: originGrants,
|
|
1081
|
+
expectedGrantRevisions,
|
|
1082
|
+
action: act,
|
|
1083
|
+
currentUrl: page.url(),
|
|
1084
|
+
});
|
|
1085
|
+
}
|
|
1086
|
+
const outcome = await runOneAction(page, act, stepTimeout, {
|
|
1087
|
+
resolveUploadArtifact,
|
|
1088
|
+
});
|
|
1089
|
+
step.detail =
|
|
1090
|
+
act.type === "navigate"
|
|
1091
|
+
? `navigated to ${redactBrowserUrl(act.url)}`
|
|
1092
|
+
: redactBrowserText(outcome.detail, 500);
|
|
1093
|
+
step.ok = true;
|
|
1094
|
+
if (outcome.uploadArtifact) {
|
|
1095
|
+
step.uploadArtifact = outcome.uploadArtifact;
|
|
1096
|
+
}
|
|
1097
|
+
if (act.screenshotPath) {
|
|
1098
|
+
step.screenshotPath = act.screenshotPath;
|
|
1099
|
+
step.screenshotRef = path.basename(act.screenshotPath);
|
|
1100
|
+
step.screenshotSha256 = digestGeneratedFile(
|
|
1101
|
+
act.screenshotPath,
|
|
1102
|
+
deps,
|
|
1103
|
+
);
|
|
1104
|
+
if (evidenceBinding && !step.screenshotSha256) {
|
|
1105
|
+
throw new Error("browser evidence screenshot digest is missing");
|
|
1106
|
+
}
|
|
1107
|
+
}
|
|
1108
|
+
if (act.downloadPath) {
|
|
1109
|
+
step.downloadPath = act.downloadPath;
|
|
1110
|
+
step.downloadRef = path.basename(act.downloadPath);
|
|
1111
|
+
step.downloadSha256 = digestGeneratedFile(act.downloadPath, deps);
|
|
1112
|
+
if (evidenceBinding && !step.downloadSha256) {
|
|
1113
|
+
throw new Error("browser evidence download digest is missing");
|
|
1114
|
+
}
|
|
1115
|
+
step.downloadSuggestedName =
|
|
1116
|
+
outcome.suggestedName || "download.bin";
|
|
1117
|
+
}
|
|
1118
|
+
} catch (err) {
|
|
1119
|
+
cleanupFailedScreenshot(act.screenshotPath, deps);
|
|
1120
|
+
cleanupFailedScreenshot(act.downloadPath, deps);
|
|
1121
|
+
step.ok = false;
|
|
1122
|
+
delete step.screenshotPath;
|
|
1123
|
+
delete step.screenshotRef;
|
|
1124
|
+
delete step.screenshotSha256;
|
|
1125
|
+
delete step.downloadPath;
|
|
1126
|
+
delete step.downloadRef;
|
|
1127
|
+
delete step.downloadSha256;
|
|
1128
|
+
delete step.downloadSuggestedName;
|
|
1129
|
+
delete step.uploadArtifact;
|
|
1130
|
+
step.detail =
|
|
1131
|
+
act.type === "navigate"
|
|
1132
|
+
? `navigation failed for ${redactBrowserUrl(act.url, 400)}`
|
|
1133
|
+
: redactScreenshotFailure(
|
|
1134
|
+
err,
|
|
1135
|
+
act.screenshotPath || act.downloadPath,
|
|
1136
|
+
500,
|
|
1137
|
+
);
|
|
1138
|
+
}
|
|
1139
|
+
step.durationMs = Date.now() - started;
|
|
1140
|
+
const pageAfter = redactBrowserUrl(page.url());
|
|
1141
|
+
result.steps.push(step);
|
|
1142
|
+
authorities.push(authority);
|
|
1143
|
+
try {
|
|
1144
|
+
const entry = {
|
|
1145
|
+
ts: new Date(started).toISOString(),
|
|
1146
|
+
action: act.type,
|
|
1147
|
+
ok: step.ok,
|
|
1148
|
+
durationMs: step.durationMs,
|
|
1149
|
+
pageBefore,
|
|
1150
|
+
pageAfter,
|
|
1151
|
+
result: step.detail,
|
|
1152
|
+
};
|
|
1153
|
+
if (sessionId) {
|
|
1154
|
+
entry.sessionId = redactBrowserText(sessionId, 160);
|
|
1155
|
+
}
|
|
1156
|
+
if (act.selector) entry.selector = truncateForAudit(act.selector);
|
|
1157
|
+
if (act.url) entry.url = redactBrowserUrl(act.url);
|
|
1158
|
+
if (act.key) entry.key = truncateForAudit(act.key, 50);
|
|
1159
|
+
if (act.artifactId) entry.uploadArtifactId = act.artifactId;
|
|
1160
|
+
if (step.ok && act.screenshotPath) {
|
|
1161
|
+
entry.screenshotRef = path.basename(act.screenshotPath);
|
|
1162
|
+
entry.screenshotSha256 = step.screenshotSha256;
|
|
1163
|
+
}
|
|
1164
|
+
if (step.ok && act.downloadPath) {
|
|
1165
|
+
entry.downloadRef = path.basename(act.downloadPath);
|
|
1166
|
+
entry.downloadSha256 = step.downloadSha256;
|
|
1167
|
+
}
|
|
1168
|
+
if (authority) {
|
|
1169
|
+
entry.originGrant = {
|
|
1170
|
+
grantId: authority.grantId,
|
|
1171
|
+
revision: authority.revision,
|
|
1172
|
+
origin: authority.origin,
|
|
1173
|
+
scope: authority.scope,
|
|
1174
|
+
};
|
|
1175
|
+
}
|
|
1176
|
+
appendActionAudit(entry, deps);
|
|
1177
|
+
} catch (err) {
|
|
1178
|
+
auditError = redactBrowserText(err.message, 300);
|
|
857
1179
|
}
|
|
858
|
-
if (
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
1180
|
+
if (!step.ok && !continueOnError) break;
|
|
1181
|
+
}
|
|
1182
|
+
|
|
1183
|
+
result.executed = result.steps.length;
|
|
1184
|
+
result.ok =
|
|
1185
|
+
result.steps.length === normalized.length &&
|
|
1186
|
+
result.steps.every((s) => s.ok);
|
|
1187
|
+
if (auditError) result.auditError = auditError;
|
|
1188
|
+
result.url = redactBrowserUrl(page.url());
|
|
1189
|
+
result.title = redactBrowserText(await page.title().catch(() => ""), 500);
|
|
1190
|
+
result.console = consoleEntries;
|
|
1191
|
+
result.network = networkEntries;
|
|
1192
|
+
if (evidenceBinding && result.steps.length > 0) {
|
|
1193
|
+
let domCaptureSucceeded = true;
|
|
1194
|
+
const sourceHtml = await page.content().catch(() => {
|
|
1195
|
+
domCaptureSucceeded = false;
|
|
1196
|
+
return "";
|
|
1197
|
+
});
|
|
1198
|
+
const normalizedCap = Math.min(
|
|
1199
|
+
Math.max(0, Number(evidenceDomCap) || 40000),
|
|
1200
|
+
DEFAULT_DOM_CAP,
|
|
1201
|
+
);
|
|
1202
|
+
const safeHtml = redactBrowserDom(sourceHtml, normalizedCap);
|
|
1203
|
+
const evidenceActions = result.steps.map((step, actionIndex) =>
|
|
1204
|
+
describeBrowserAction(
|
|
1205
|
+
normalized[actionIndex],
|
|
1206
|
+
step,
|
|
1207
|
+
authorities[actionIndex],
|
|
1208
|
+
actionIndex,
|
|
1209
|
+
),
|
|
1210
|
+
);
|
|
1211
|
+
result.evidence = createBrowserEvidenceEnvelope({
|
|
1212
|
+
binding: evidenceBinding,
|
|
1213
|
+
originPermissions: authorities.filter(Boolean),
|
|
1214
|
+
actions: evidenceActions,
|
|
1215
|
+
consoleEntries,
|
|
1216
|
+
networkEntries,
|
|
1217
|
+
pageUrl: page.url(),
|
|
1218
|
+
pageTitle: result.title,
|
|
1219
|
+
domSnapshot: {
|
|
1220
|
+
html: safeHtml,
|
|
1221
|
+
sourceChars: sourceHtml.length,
|
|
1222
|
+
cap: normalizedCap,
|
|
1223
|
+
truncated: sourceHtml.length > normalizedCap,
|
|
1224
|
+
captureSucceeded: domCaptureSucceeded,
|
|
1225
|
+
redaction: browserDomRedactionMetadata(sourceHtml),
|
|
1226
|
+
},
|
|
1227
|
+
screenshots: result.steps
|
|
1228
|
+
.map((step, actionIndex) => ({
|
|
1229
|
+
actionIndex,
|
|
1230
|
+
digest: step.screenshotSha256,
|
|
1231
|
+
}))
|
|
1232
|
+
.filter((row) => row.digest),
|
|
1233
|
+
downloads: result.steps
|
|
1234
|
+
.map((step, actionIndex) => ({
|
|
1235
|
+
actionIndex,
|
|
1236
|
+
digest: step.downloadSha256,
|
|
1237
|
+
suggestedName: step.downloadSuggestedName,
|
|
1238
|
+
}))
|
|
1239
|
+
.filter((row) => row.digest),
|
|
1240
|
+
replay,
|
|
1241
|
+
observationCaptureAvailable: canObserve,
|
|
1242
|
+
});
|
|
1243
|
+
if (!domCaptureSucceeded || !canObserve) {
|
|
1244
|
+
result.ok = false;
|
|
1245
|
+
result.evidenceIncomplete = true;
|
|
1246
|
+
result.retrySafe = false;
|
|
1247
|
+
result.recovery =
|
|
1248
|
+
"Browser actions may have completed, but observation evidence capture was incomplete; do not retry side effects automatically.";
|
|
863
1249
|
}
|
|
864
|
-
appendActionAudit(entry, deps);
|
|
865
|
-
} catch (err) {
|
|
866
|
-
auditError = redactBrowserText(err.message, 300);
|
|
867
1250
|
}
|
|
868
|
-
|
|
1251
|
+
return result;
|
|
1252
|
+
} catch (error) {
|
|
1253
|
+
for (const step of result.steps || []) {
|
|
1254
|
+
cleanupFailedScreenshot(step.screenshotPath, deps);
|
|
1255
|
+
cleanupFailedScreenshot(step.downloadPath, deps);
|
|
1256
|
+
}
|
|
1257
|
+
throw error;
|
|
1258
|
+
} finally {
|
|
1259
|
+
if (canObserve) {
|
|
1260
|
+
page.off("console", onConsole);
|
|
1261
|
+
page.off("requestfailed", onRequestFailed);
|
|
1262
|
+
page.off("response", onResponse);
|
|
1263
|
+
}
|
|
869
1264
|
}
|
|
870
|
-
|
|
871
|
-
result.executed = result.steps.length;
|
|
872
|
-
result.ok =
|
|
873
|
-
result.steps.length === normalized.length &&
|
|
874
|
-
result.steps.every((s) => s.ok);
|
|
875
|
-
if (auditError) result.auditError = auditError;
|
|
876
|
-
result.url = redactBrowserUrl(page.url());
|
|
877
|
-
result.title = redactBrowserText(await page.title().catch(() => ""), 500);
|
|
878
|
-
return result;
|
|
879
1265
|
} finally {
|
|
880
1266
|
// connectOverCDP close() disconnects the client; the browser lives on.
|
|
881
1267
|
await browser.close().catch(() => {});
|