chainlesschain 0.162.162 → 0.162.164
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +3 -1
- package/src/assets/web-panel/assets/{AIOps-ClI2ALgv.js → AIOps-BqRkaHGb.js} +1 -1
- package/src/assets/web-panel/assets/{ActionButton-BDjRcJaK.js → ActionButton-DWO7OZLC.js} +1 -1
- package/src/assets/web-panel/assets/{Analytics-DikSRsaT.js → Analytics-C5oaVezM.js} +3 -3
- package/src/assets/web-panel/assets/{AppLayout-9mLhMFmz.js → AppLayout-CsUESDoZ.js} +3 -3
- package/src/assets/web-panel/assets/{Artifacts-BSr88BrH.js → Artifacts-Dragqu5k.js} +1 -1
- package/src/assets/web-panel/assets/{Audit-CdvyimOF.js → Audit-CQsaHctY.js} +1 -1
- package/src/assets/web-panel/assets/{BackgroundAgents-D3_hCedh.js → BackgroundAgents-DdG1jNSM.js} +1 -1
- package/src/assets/web-panel/assets/{Backup-Cyvv9RHf.js → Backup-DN6sjK4z.js} +1 -1
- package/src/assets/web-panel/assets/{BaseInput-BZM14kt8.js → BaseInput-Cxm_ZTCl.js} +1 -1
- package/src/assets/web-panel/assets/{Chat-DrQSokVz.js → Chat-DVXGmA56.js} +6 -6
- package/src/assets/web-panel/assets/{ChatBubbleRenderer-HwJR-C6B.js → ChatBubbleRenderer-BdaCL_iE.js} +1 -1
- package/src/assets/web-panel/assets/{Checkbox-bFLYeWMV.js → Checkbox-DEc4vD_A.js} +1 -1
- package/src/assets/web-panel/assets/{Codegen-7mN7xJXT.js → Codegen-CKiTcv36.js} +1 -1
- package/src/assets/web-panel/assets/{Col-DAB5ZWm7.js → Col-Br9UUNN0.js} +1 -1
- package/src/assets/web-panel/assets/{Community-B_1TymaK.js → Community-NeUH_70T.js} +1 -1
- package/src/assets/web-panel/assets/{Compact-Bq1uSWLJ.js → Compact-BqR-z2Df.js} +1 -1
- package/src/assets/web-panel/assets/{Compliance-CyiE1jEe.js → Compliance-DcWB_SgZ.js} +1 -1
- package/src/assets/web-panel/assets/{Cowork-Cpk0D7vm.js → Cowork-BfhgcudF.js} +2 -2
- package/src/assets/web-panel/assets/{Cron-d_Lv3rdB.js → Cron-LaU29LgD.js} +2 -2
- package/src/assets/web-panel/assets/{Crosschain-DMqhHjMR.js → Crosschain-B2ZB9uwW.js} +1 -1
- package/src/assets/web-panel/assets/{DID-Br-oPuBw.js → DID-BuHl38A2.js} +2 -2
- package/src/assets/web-panel/assets/{Dashboard-CfLW_RaP.js → Dashboard-m627yqvZ.js} +2 -2
- package/src/assets/web-panel/assets/{Dropdown-DZnTOExV.js → Dropdown-C7bxF8F5.js} +1 -1
- package/src/assets/web-panel/assets/{EmailListRenderer-DHIUHDv1.js → EmailListRenderer-DPLPQzLp.js} +1 -1
- package/src/assets/web-panel/assets/{FamilyGuardDashboard-Bw3t3myu.js → FamilyGuardDashboard-cOiH6ike.js} +1 -1
- package/src/assets/web-panel/assets/{Federation-D9cpQPAY.js → Federation-Bvkzg88z.js} +1 -1
- package/src/assets/web-panel/assets/{FormItemContext-Jdczr9Mr.js → FormItemContext-BTsiA7h_.js} +1 -1
- package/src/assets/web-panel/assets/{GenericCardRenderer-BT8ZMpC5.js → GenericCardRenderer-BGnVE5qu.js} +1 -1
- package/src/assets/web-panel/assets/{Git-DjARip2b.js → Git-DS_j6r25.js} +2 -2
- package/src/assets/web-panel/assets/{Governance-BgWbSRcj.js → Governance-CKRG3Rsl.js} +1 -1
- package/src/assets/web-panel/assets/{Inference-CfouJciM.js → Inference-DW8INoNQ.js} +1 -1
- package/src/assets/web-panel/assets/{KnowledgeGraph-50l7mrAr.js → KnowledgeGraph-BhEYeFhC.js} +1 -1
- package/src/assets/web-panel/assets/{Logs-rq50H84B.js → Logs-wqi0uxuR.js} +2 -2
- package/src/assets/web-panel/assets/{MarkdownRenderer-DUPIEdQH.js → MarkdownRenderer-DS8dE8ty.js} +1 -1
- package/src/assets/web-panel/assets/{Marketplace-BYgPDmHU.js → Marketplace-CMlTuPLT.js} +1 -1
- package/src/assets/web-panel/assets/{McpTools-5DVgcFdx.js → McpTools-CSx4m5JJ.js} +3 -3
- package/src/assets/web-panel/assets/{Memory-B70LPk7f.js → Memory-vnQCUy-_.js} +2 -2
- package/src/assets/web-panel/assets/{MobileBridge-De8ssA5Y.js → MobileBridge-BV2NA742.js} +1 -1
- package/src/assets/web-panel/assets/MobileProjects-Bw0k8e1B.js +1 -0
- package/src/assets/web-panel/assets/{Mtc-RJsOTsIA.js → Mtc-CzK_FPB1.js} +2 -2
- package/src/assets/web-panel/assets/{MtcAudit-Crh-6BEK.js → MtcAudit-Cb7G9oL3.js} +5 -5
- package/src/assets/web-panel/assets/{Multisig-wuoR5l3x.js → Multisig-qJxlUyzr.js} +3 -3
- package/src/assets/web-panel/assets/{NLProgramming-U2Mji4fZ.js → NLProgramming-DqaBJ7Te.js} +1 -1
- package/src/assets/web-panel/assets/{Notes-CdTZAyDH.js → Notes-CwhTJkRi.js} +3 -3
- package/src/assets/web-panel/assets/{NotificationSettings-C-FucWE7.js → NotificationSettings-YDs4Fal4.js} +1 -1
- package/src/assets/web-panel/assets/{OrderTableRenderer-DCYFTeaI.js → OrderTableRenderer-CvEALpq7.js} +1 -1
- package/src/assets/web-panel/assets/{Organization-an3OF85L.js → Organization-CEmQm9n8.js} +4 -4
- package/src/assets/web-panel/assets/{Overflow-C-1vmYFe.js → Overflow-zby_P_LA.js} +1 -1
- package/src/assets/web-panel/assets/{P2P-Cwh47xKg.js → P2P-DsmDNFNT.js} +2 -2
- package/src/assets/web-panel/assets/{PdhVaultBrowser-Dm38GT0k.js → PdhVaultBrowser-FWwFlZGF.js} +3 -3
- package/src/assets/web-panel/assets/{Permissions-DS2Y8awD.js → Permissions-DuzQ6me5.js} +4 -4
- package/src/assets/web-panel/assets/{PersonalDataHub-BQNziA0I.js → PersonalDataHub-DZbeN613.js} +3 -3
- package/src/assets/web-panel/assets/{Pipeline-DF85L1ue.js → Pipeline-CoNSVAUS.js} +1 -1
- package/src/assets/web-panel/assets/{Privacy-BuLornF-.js → Privacy-OYJEAaMC.js} +1 -1
- package/src/assets/web-panel/assets/{ProjectInit-C3Y31IwX.js → ProjectInit-CWIapV--.js} +2 -2
- package/src/assets/web-panel/assets/{ProjectSettings-BCPiI8aD.js → ProjectSettings-CX8xMxZn.js} +2 -2
- package/src/assets/web-panel/assets/{Projects-BZ6s4k6g.js → Projects-BLCN8YyY.js} +1 -1
- package/src/assets/web-panel/assets/{Providers-D3Wp022X.js → Providers-BV_sxp75.js} +1 -1
- package/src/assets/web-panel/assets/{QrScannerModal-CUJfQkH4.js → QrScannerModal-BTnRrHRp.js} +1 -1
- package/src/assets/web-panel/assets/{QuickAsk-UAc0EO8l.js → QuickAsk-D1KZg018.js} +1 -1
- package/src/assets/web-panel/assets/{Recommend-BaxKupcs.js → Recommend-COm5cuNg.js} +1 -1
- package/src/assets/web-panel/assets/{RemoteSession-C75NNnXZ.js → RemoteSession-0saWEkYI.js} +2 -2
- package/src/assets/web-panel/assets/{Reputation-BVXynZAk.js → Reputation-Dr7iKLS2.js} +1 -1
- package/src/assets/web-panel/assets/{Row-C5MRZ0bi.js → Row-C1vpPBcr.js} +1 -1
- package/src/assets/web-panel/assets/{RssFeed-DAKkEYka.js → RssFeed-CxlWrtNj.js} +3 -3
- package/src/assets/web-panel/assets/{Search-DvWv9YOA.js → Search-BT2V8dPN.js} +1 -1
- package/src/assets/web-panel/assets/{Security-B5peafW1.js → Security-bm9V3c5k.js} +4 -4
- package/src/assets/web-panel/assets/{Services-UQmvPEaG.js → Services-CocH1yyX.js} +2 -2
- package/src/assets/web-panel/assets/{Skeleton-C3bhWpuF.js → Skeleton-DHItlMdd.js} +1 -1
- package/src/assets/web-panel/assets/{Skills-DfkKf9_H.js → Skills-C59sLc4w.js} +1 -1
- package/src/assets/web-panel/assets/{Sla-DqMSjlBM.js → Sla-Crj4BMc8.js} +1 -1
- package/src/assets/web-panel/assets/{SpeechSettings-C-WbxWUX.js → SpeechSettings-DX4KFceY.js} +1 -1
- package/src/assets/web-panel/assets/{SyncSettings-CYuHZ4N2.js → SyncSettings-BD_iwQLr.js} +2 -2
- package/src/assets/web-panel/assets/{Tasks-CyeAyB9d.js → Tasks-BmCTzYsH.js} +1 -1
- package/src/assets/web-panel/assets/{Templates-BTCeQnpq.js → Templates-BGUaxZYl.js} +1 -1
- package/src/assets/web-panel/assets/{Tenant-BifQuA9Y.js → Tenant-BS0tJx2O.js} +1 -1
- package/src/assets/web-panel/assets/{Terminal-Bx_9Bocf.js → Terminal--tHSAsld.js} +2 -2
- package/src/assets/web-panel/assets/{TimelineRenderer-BX0RFCOm.js → TimelineRenderer-D9-MFxS4.js} +1 -1
- package/src/assets/web-panel/assets/{Tokens-CGHrCmUS.js → Tokens-CiCB485f.js} +1 -1
- package/src/assets/web-panel/assets/{Trigger-DlqaY4SO.js → Trigger-SmCFuW_l.js} +1 -1
- package/src/assets/web-panel/assets/{Trust-DfOmj-ay.js → Trust-DkK3Rqe0.js} +1 -1
- package/src/assets/web-panel/assets/{UkeySign-B72jH5fE.js → UkeySign-CDM4LGdB.js} +1 -1
- package/src/assets/web-panel/assets/{VideoEditing-BT-63fqv.js → VideoEditing-IDB7DJXt.js} +1 -1
- package/src/assets/web-panel/assets/{Wallet-Dsul3sHr.js → Wallet-Bikj5cmt.js} +4 -4
- package/src/assets/web-panel/assets/{WebAuthn-Izkwwdgy.js → WebAuthn-C9abWlAw.js} +2 -2
- package/src/assets/web-panel/assets/{WorkflowEditor-D3FHSiH1.js → WorkflowEditor-DrbRKVky.js} +1 -1
- package/src/assets/web-panel/assets/{chat-CmVz5z3U.js → chat-CccCSDQf.js} +1 -1
- package/src/assets/web-panel/assets/{colors-CuLgyJBp.js → colors-CXObXLhb.js} +1 -1
- package/src/assets/web-panel/assets/{compact-item-Ml1YkXpw.js → compact-item-B5kfYAxC.js} +1 -1
- package/src/assets/web-panel/assets/{createContext-BvjJ48bs.js → createContext-DmiPBfip.js} +1 -1
- package/src/assets/web-panel/assets/devWarning-CvKrADNa.js +1 -0
- package/src/assets/web-panel/assets/{hasIn-DVC0157C.js → hasIn-Q6lYteAc.js} +1 -1
- package/src/assets/web-panel/assets/{index-C229ABfy.js → index-25Wlqps4.js} +1 -1
- package/src/assets/web-panel/assets/{index-Ck_52dz_.js → index-33U29UNV.js} +1 -1
- package/src/assets/web-panel/assets/{index-QXzlT9KD.js → index-4oXBDczR.js} +1 -1
- package/src/assets/web-panel/assets/{index-4SBta3d7.js → index-BGYXvMc5.js} +1 -1
- package/src/assets/web-panel/assets/{index-B0S8NzO6.js → index-BJCCa0s_.js} +1 -1
- package/src/assets/web-panel/assets/{index-DJyN28Et.js → index-BJJ5DhKI.js} +1 -1
- package/src/assets/web-panel/assets/{index-DC_OI9H2.js → index-BLOzViJ6.js} +1 -1
- package/src/assets/web-panel/assets/{index-C_XuRgGn.js → index-BRrbUMoG.js} +1 -1
- package/src/assets/web-panel/assets/{index-wvb_FtaV.js → index-BUFpT17Q.js} +1 -1
- package/src/assets/web-panel/assets/{index-KMqhcF7l.js → index-BkRDiqGc.js} +1 -1
- package/src/assets/web-panel/assets/{index-vG4XSJ1d.js → index-BsfvW6my.js} +1 -1
- package/src/assets/web-panel/assets/{index-DYXHFr4r.js → index-By-kfPED.js} +1 -1
- package/src/assets/web-panel/assets/{index-DuGszaWY.js → index-BzFTcQsc.js} +1 -1
- package/src/assets/web-panel/assets/{index-CXkJH-lK.js → index-C2FXdboq.js} +1 -1
- package/src/assets/web-panel/assets/{index-Dt5KMjwJ.js → index-C5XOlTHN.js} +1 -1
- package/src/assets/web-panel/assets/{index-4wcgvPoF.js → index-CLhBDS2w.js} +1 -1
- package/src/assets/web-panel/assets/{index-C_H_S-DY.js → index-CMq7UeoT.js} +1 -1
- package/src/assets/web-panel/assets/{index-BydjdZnl.js → index-CVMTWNKl.js} +1 -1
- package/src/assets/web-panel/assets/{index-BjHBpRYZ.js → index-Caghi-yL.js} +1 -1
- package/src/assets/web-panel/assets/{index-Bzr0rB5T.js → index-CdB6FuSH.js} +1 -1
- package/src/assets/web-panel/assets/{index-BbPtzB8M.js → index-CoK8-kWQ.js} +1 -1
- package/src/assets/web-panel/assets/{index-BUt8kTIB.js → index-CsO3GUAY.js} +1 -1
- package/src/assets/web-panel/assets/{index-C_vRgTl9.js → index-CwZBa20Z.js} +1 -1
- package/src/assets/web-panel/assets/{index-DukDVXCv.js → index-D-kASucw.js} +1 -1
- package/src/assets/web-panel/assets/{index-BbepULSc.js → index-D8eBw7vW.js} +1 -1
- package/src/assets/web-panel/assets/{index-BfuKP4qe.js → index-D9wgifk4.js} +1 -1
- package/src/assets/web-panel/assets/{index-BiC6dQZG.js → index-DAYCTc07.js} +1 -1
- package/src/assets/web-panel/assets/{index-CmFNvNLZ.js → index-DF6Q8l2h.js} +1 -1
- package/src/assets/web-panel/assets/index-DTdFUWc_.js +1 -0
- package/src/assets/web-panel/assets/{index-KjgKdZe-.js → index-DgTLApS9.js} +1 -1
- package/src/assets/web-panel/assets/{index-BpFJHiTv.js → index-DgxVJ8pg.js} +1 -1
- package/src/assets/web-panel/assets/{index-Cw0Rjy87.js → index-DlJvIqoR.js} +1 -1
- package/src/assets/web-panel/assets/{index-CFYVQMvu.js → index-Dw4eHfsG.js} +1 -1
- package/src/assets/web-panel/assets/{index-CudmwNBV.js → index-Rh-ytwow.js} +3 -3
- package/src/assets/web-panel/assets/{index-DDGaOWBV.js → index-StAEt2pq.js} +1 -1
- package/src/assets/web-panel/assets/{index-D3lD5qap.js → index-bzfw951k.js} +1 -1
- package/src/assets/web-panel/assets/{index-BzG-x9p4.js → index-fSUMbMvv.js} +1 -1
- package/src/assets/web-panel/assets/{index-B_EvG_GW.js → index-tFHy5Wol.js} +1 -1
- package/src/assets/web-panel/assets/index-zQ0M_kz-.js +1 -0
- package/src/assets/web-panel/assets/{initDefaultProps-BDgd2YZ8.js → initDefaultProps-BNq7X72N.js} +1 -1
- package/src/assets/web-panel/assets/{motion-ErtrXdYe.js → motion-DOf9viTc.js} +1 -1
- package/src/assets/web-panel/assets/{move-_GPPNCgs.js → move-CRHTcW4x.js} +1 -1
- package/src/assets/web-panel/assets/{mtc-parser-DRLL-8V2.js → mtc-parser-Cyoozgm2.js} +1 -1
- package/src/assets/web-panel/assets/{omit-p86sl023.js → omit-6-TjFNru.js} +1 -1
- package/src/assets/web-panel/assets/{pickAttrs-BTu7cMLH.js → pickAttrs-DlUmO1zi.js} +1 -1
- package/src/assets/web-panel/assets/{placementArrow-BEKz6Isr.js → placementArrow-CFOL8mIR.js} +1 -1
- package/src/assets/web-panel/assets/{responsiveObserve-Ck42c9MP.js → responsiveObserve-BtaFK5zd.js} +1 -1
- package/src/assets/web-panel/assets/{slide-1CEvuPsB.js → slide-BIbc2bur.js} +1 -1
- package/src/assets/web-panel/assets/{statusUtils-BDfdx1GJ.js → statusUtils-BXX9d6-w.js} +1 -1
- package/src/assets/web-panel/assets/{styleChecker-B4K46m5w.js → styleChecker-BGv10Bpn.js} +1 -1
- package/src/assets/web-panel/assets/{useFlexGapSupport-DoIVpJNq.js → useFlexGapSupport-bKnKT8tw.js} +1 -1
- package/src/assets/web-panel/assets/{useFs-DdAWi-ln.js → useFs-FY4IL4jn.js} +1 -1
- package/src/assets/web-panel/assets/{usePersonalDataHub-iUu6k8dx.js → usePersonalDataHub-Bwl9TEgu.js} +1 -1
- package/src/assets/web-panel/assets/{vnode-DZz4R6Tw.js → vnode-BM3OHxAT.js} +1 -1
- package/src/assets/web-panel/assets/{zoom-DXlf9sA3.js → zoom-D1fbFiHB.js} +1 -1
- package/src/assets/web-panel/index.html +1 -1
- package/src/commands/agenda.js +320 -32
- package/src/commands/agent.js +105 -11
- package/src/commands/context.js +125 -14
- package/src/commands/doctor.js +85 -1
- package/src/commands/hook.js +181 -7
- package/src/commands/plugin.js +456 -1
- package/src/commands/review.js +363 -2
- package/src/commands/session.js +191 -0
- package/src/commands/team.js +19 -1
- package/src/data/changelog.json +10 -4
- package/src/gateways/ws/remote-session-protocol.js +39 -0
- package/src/gateways/ws/session-protocol.js +55 -2
- package/src/gateways/ws/ws-agent-handler.js +50 -0
- package/src/harness/jsonl-session-store.js +60 -0
- package/src/harness/worktree-isolator.js +140 -5
- package/src/lib/agent-schedule-store.js +318 -10
- package/src/lib/agent-team/team-worktree.js +37 -2
- package/src/lib/agent-tool-admission.js +125 -0
- package/src/lib/app-preview.js +360 -0
- package/src/lib/async-hook-supervisor.cjs +54 -0
- package/src/lib/capability-manifest.js +319 -0
- package/src/lib/capability-negotiation.js +23 -12
- package/src/lib/channels/channel-manager.js +54 -2
- package/src/lib/cli-context-engineering.js +9 -2
- package/src/lib/context-breakdown.js +116 -0
- package/src/lib/diagnostic-bundle.js +230 -0
- package/src/lib/doctor-checkup.js +275 -4
- package/src/lib/execution-location.js +286 -0
- package/src/lib/headless-manifest.js +13 -40
- package/src/lib/hook-event-log.cjs +193 -0
- package/src/lib/hook-stats-store.cjs +197 -0
- package/src/lib/idempotency.js +109 -0
- package/src/lib/interaction-adapter.js +34 -4
- package/src/lib/json-schema-output.js +46 -12
- package/src/lib/json-schema-validate.js +143 -3
- package/src/lib/jsonl-session-store.js +1 -0
- package/src/lib/lsp/__tests__/lsp-manager.test.js +58 -0
- package/src/lib/lsp/lsp-manager.js +56 -0
- package/src/lib/monitor-event.js +290 -0
- package/src/lib/operation-fingerprint.js +217 -0
- package/src/lib/plugin-runtime/bin.js +37 -0
- package/src/lib/plugin-runtime/capabilities.js +19 -0
- package/src/lib/plugin-runtime/capability-consent.js +203 -0
- package/src/lib/plugin-runtime/hooks.js +37 -0
- package/src/lib/plugin-runtime/lsp.js +38 -0
- package/src/lib/plugin-runtime/mcp.js +40 -0
- package/src/lib/plugin-runtime/monitors.js +37 -0
- package/src/lib/plugin-runtime/plugin-options.js +151 -0
- package/src/lib/plugin-runtime/policy.js +62 -0
- package/src/lib/plugin-runtime/scopes.js +20 -4
- package/src/lib/pr-automation-policy.js +224 -0
- package/src/lib/project-instructions.js +128 -0
- package/src/lib/remote-approval-bridge.js +66 -13
- package/src/lib/repl-goal.js +132 -0
- package/src/lib/repl-rewind.js +153 -0
- package/src/lib/review-comment-anchor.js +241 -0
- package/src/lib/review-pipeline.js +46 -7
- package/src/lib/runtime-checkup.js +221 -0
- package/src/lib/schedule-planner.js +17 -1
- package/src/lib/secret-scan.js +409 -0
- package/src/lib/session-branch.js +0 -0
- package/src/lib/settings-hook-events.cjs +13 -0
- package/src/lib/settings-loader.cjs +42 -0
- package/src/lib/side-effect-ledger-store.js +88 -0
- package/src/lib/side-effect-ledger.js +401 -0
- package/src/lib/skill-loader.js +32 -0
- package/src/lib/skill-path-scope.js +63 -0
- package/src/lib/sub-agent-context.js +110 -9
- package/src/lib/sub-agent-registry.js +19 -0
- package/src/lib/subagent-inheritance.js +112 -0
- package/src/lib/telemetry/span-recorder.js +17 -4
- package/src/lib/terminal-context.js +195 -0
- package/src/lib/turn-binding-store.js +119 -0
- package/src/lib/unattended-action-policy.js +182 -0
- package/src/lib/worktree-cleanup-safety.js +138 -0
- package/src/lib/worktree-isolator.js +8 -0
- package/src/lib/worktree-sparse.js +131 -0
- package/src/repl/agent-repl.js +280 -30
- package/src/repl/permission-tier.js +28 -0
- package/src/runtime/agent-core.js +565 -21
- package/src/runtime/coding-agent-contract-shared.cjs +60 -1
- package/src/runtime/diagnostics.js +60 -0
- package/src/runtime/headless-runner.js +393 -1
- package/src/runtime/headless-stream.js +60 -1
- package/src/assets/web-panel/assets/MobileProjects-D10VISps.js +0 -1
- package/src/assets/web-panel/assets/devWarning-DhiGrbsh.js +0 -1
- package/src/assets/web-panel/assets/index-B7a1TrU6.js +0 -1
- package/src/assets/web-panel/assets/index-C2B1KRja.js +0 -1
|
@@ -0,0 +1,230 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* De-identified diagnostic bundle assembler + mandatory secret-scan export gate
|
|
3
|
+
* (IDE gap P1-9 "脱敏诊断包" of CLAUDE_CODE_IDE_INCREMENTAL_GAP_ANALYSIS_2026-
|
|
4
|
+
* 07-13.md).
|
|
5
|
+
*
|
|
6
|
+
* When an IDE session misbehaves, a one-click diagnostic export is the fastest
|
|
7
|
+
* path to a fix — but a naive dump of "everything about the session" leaks
|
|
8
|
+
* source, keys and terminal output. The gap doc pins the contract exactly: a
|
|
9
|
+
* bundle carries version, platform, capability, connection state, redaction
|
|
10
|
+
* events, trace, reconnect history, and process/port/lockfile/worktree
|
|
11
|
+
* summaries; it does NOT carry source code, API keys, cookies, full environment
|
|
12
|
+
* variables, or unpermitted terminal output; and "导出前必须运行 Secret Scan".
|
|
13
|
+
*
|
|
14
|
+
* This module assembles that bundle from caller-supplied signals and runs the
|
|
15
|
+
* §8.1 recall-first scanner ([[secret-scan.js]]) as a fail-closed export gate.
|
|
16
|
+
* It reuses the canonical capability projection ([[capability-manifest.js]]) so
|
|
17
|
+
* the bundle never invents a second capability truth.
|
|
18
|
+
*
|
|
19
|
+
* Pure: no fs / process / clock / RNG — the caller collects the raw signals
|
|
20
|
+
* (which processes, which ports, the env object) and this module shapes, de-
|
|
21
|
+
* identifies and gates them. Timestamps, if any, are passed in.
|
|
22
|
+
*/
|
|
23
|
+
|
|
24
|
+
import { redactSecrets, scanSecrets, SURFACES } from "./secret-scan.js";
|
|
25
|
+
import { buildCompatFixture } from "./capability-manifest.js";
|
|
26
|
+
import { isSecretEnvName } from "./credential-guard.js";
|
|
27
|
+
|
|
28
|
+
/** The categories deliberately EXCLUDED from every bundle (never assembled). */
|
|
29
|
+
export const EXCLUDED_BY_DEFAULT = Object.freeze([
|
|
30
|
+
"source code body",
|
|
31
|
+
"API keys / tokens",
|
|
32
|
+
"cookies",
|
|
33
|
+
"full environment variable values",
|
|
34
|
+
"unpermitted terminal output",
|
|
35
|
+
]);
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Summarize an environment WITHOUT any values: variable NAMES only, with the
|
|
39
|
+
* secret-named ones flagged (via isSecretEnvName). Honors the contract "默认不
|
|
40
|
+
* 包含完整环境变量" — a name is metadata, a value may be a credential.
|
|
41
|
+
* @param {object} env
|
|
42
|
+
* @returns {{count:number, names:string[], secretNames:string[]}}
|
|
43
|
+
*/
|
|
44
|
+
export function summarizeEnv(env) {
|
|
45
|
+
if (!env || typeof env !== "object") {
|
|
46
|
+
return { count: 0, names: [], secretNames: [] };
|
|
47
|
+
}
|
|
48
|
+
const names = Object.keys(env).sort();
|
|
49
|
+
const secretNames = names.filter((n) => isSecretEnvName(n));
|
|
50
|
+
return { count: names.length, names, secretNames };
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/** Recursively redact every string in a value (arrays/objects walked). */
|
|
54
|
+
function deepRedact(value, depth = 0) {
|
|
55
|
+
if (depth > 64) return value;
|
|
56
|
+
if (typeof value === "string") return redactSecrets(value);
|
|
57
|
+
if (Array.isArray(value)) return value.map((v) => deepRedact(v, depth + 1));
|
|
58
|
+
if (value && typeof value === "object") {
|
|
59
|
+
const out = {};
|
|
60
|
+
for (const k of Object.keys(value))
|
|
61
|
+
out[k] = deepRedact(value[k], depth + 1);
|
|
62
|
+
return out;
|
|
63
|
+
}
|
|
64
|
+
return value;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Recursively scan a value for any residual secret, returning findings with a
|
|
69
|
+
* dotted path so the gate can name WHERE a leak survived. Read-only.
|
|
70
|
+
* @returns {Array<{path:string, category:string}>}
|
|
71
|
+
*/
|
|
72
|
+
export function scanBundleForSecrets(value, path = "", depth = 0) {
|
|
73
|
+
if (depth > 64) return [];
|
|
74
|
+
const findings = [];
|
|
75
|
+
if (typeof value === "string") {
|
|
76
|
+
for (const f of scanSecrets(value)) {
|
|
77
|
+
findings.push({ path: path || "(root)", category: f.category });
|
|
78
|
+
}
|
|
79
|
+
} else if (Array.isArray(value)) {
|
|
80
|
+
value.forEach((v, i) =>
|
|
81
|
+
findings.push(...scanBundleForSecrets(v, `${path}[${i}]`, depth + 1)),
|
|
82
|
+
);
|
|
83
|
+
} else if (value && typeof value === "object") {
|
|
84
|
+
for (const k of Object.keys(value)) {
|
|
85
|
+
findings.push(
|
|
86
|
+
...scanBundleForSecrets(value[k], path ? `${path}.${k}` : k, depth + 1),
|
|
87
|
+
);
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
return findings;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
const asArray = (v) => (Array.isArray(v) ? v : []);
|
|
94
|
+
const asString = (v) =>
|
|
95
|
+
typeof v === "string" ? v : v == null ? "" : String(v);
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* Assemble the diagnostic bundle from caller-supplied signals, de-identifying
|
|
99
|
+
* as it goes. Free-text fields are secret-redacted; env is reduced to names;
|
|
100
|
+
* terminal output is included ONLY with explicit permission (and still
|
|
101
|
+
* redacted). Excluded categories are never assembled in the first place.
|
|
102
|
+
*
|
|
103
|
+
* @param {object} input
|
|
104
|
+
* @param {string} [input.version]
|
|
105
|
+
* @param {object|string} [input.platform]
|
|
106
|
+
* @param {object} [input.capability] defaults to the canonical compat fixture
|
|
107
|
+
* @param {string} [input.connectionState]
|
|
108
|
+
* @param {Array} [input.reconnectHistory] [{at?, reason?}] — reasons redacted
|
|
109
|
+
* @param {object} [input.trace] {traceId, spanCount} summary (no content)
|
|
110
|
+
* @param {Array} [input.redactionEvents] [{surface, category, count}] — what was scrubbed
|
|
111
|
+
* @param {Array} [input.processes] [{pid, name, cpu?}] metadata
|
|
112
|
+
* @param {Array<number|object>} [input.ports]
|
|
113
|
+
* @param {Array<string>} [input.lockfiles]
|
|
114
|
+
* @param {Array} [input.worktrees] [{path, branch}]
|
|
115
|
+
* @param {object} [input.env] reduced to names only
|
|
116
|
+
* @param {string} [input.terminalOutput] included only when opts.includeTerminalOutput
|
|
117
|
+
* @param {string} [input.notes]
|
|
118
|
+
* @param {object} [opts]
|
|
119
|
+
* @param {boolean} [opts.includeTerminalOutput=false]
|
|
120
|
+
* @returns {object} the assembled (already-redacted) bundle
|
|
121
|
+
*/
|
|
122
|
+
export function buildDiagnosticBundle(input = {}, opts = {}) {
|
|
123
|
+
const includeTerminal = opts.includeTerminalOutput === true;
|
|
124
|
+
|
|
125
|
+
const redactionEvents = asArray(input.redactionEvents)
|
|
126
|
+
.map((e) => ({
|
|
127
|
+
surface: SURFACES.includes(e?.surface) ? e.surface : "unknown",
|
|
128
|
+
category: asString(e?.category),
|
|
129
|
+
count: Number(e?.count) || 0,
|
|
130
|
+
}))
|
|
131
|
+
.filter((e) => e.category);
|
|
132
|
+
|
|
133
|
+
const bundle = {
|
|
134
|
+
schema: "cc-diagnostic-bundle/v1",
|
|
135
|
+
meta: {
|
|
136
|
+
version: asString(input.version) || null,
|
|
137
|
+
platform:
|
|
138
|
+
input.platform && typeof input.platform === "object"
|
|
139
|
+
? input.platform
|
|
140
|
+
: asString(input.platform) || null,
|
|
141
|
+
excluded: [...EXCLUDED_BY_DEFAULT],
|
|
142
|
+
},
|
|
143
|
+
capability:
|
|
144
|
+
input.capability && typeof input.capability === "object"
|
|
145
|
+
? input.capability
|
|
146
|
+
: buildCompatFixture(),
|
|
147
|
+
connection: {
|
|
148
|
+
state: asString(input.connectionState) || null,
|
|
149
|
+
reconnectHistory: asArray(input.reconnectHistory).map((r) => ({
|
|
150
|
+
at: r?.at ?? null,
|
|
151
|
+
reason: asString(r?.reason),
|
|
152
|
+
})),
|
|
153
|
+
},
|
|
154
|
+
trace: input.trace
|
|
155
|
+
? {
|
|
156
|
+
traceId: asString(input.trace.traceId) || null,
|
|
157
|
+
spanCount: Number(input.trace.spanCount) || 0,
|
|
158
|
+
}
|
|
159
|
+
: null,
|
|
160
|
+
redactionEvents,
|
|
161
|
+
runtime: {
|
|
162
|
+
processes: asArray(input.processes).map((p) => ({
|
|
163
|
+
pid: p?.pid ?? null,
|
|
164
|
+
name: asString(p?.name),
|
|
165
|
+
cpu: p?.cpu ?? null,
|
|
166
|
+
})),
|
|
167
|
+
ports: asArray(input.ports).map((p) =>
|
|
168
|
+
typeof p === "object" && p
|
|
169
|
+
? { port: Number(p.port) || null, proto: asString(p.proto) || null }
|
|
170
|
+
: { port: Number(p) || null, proto: null },
|
|
171
|
+
),
|
|
172
|
+
lockfiles: asArray(input.lockfiles).map((f) => asString(f)),
|
|
173
|
+
worktrees: asArray(input.worktrees).map((w) => ({
|
|
174
|
+
path: asString(w?.path),
|
|
175
|
+
branch: asString(w?.branch) || null,
|
|
176
|
+
})),
|
|
177
|
+
},
|
|
178
|
+
// env is reduced to NAMES only up front (values never enter the bundle).
|
|
179
|
+
env: summarizeEnv(input.env),
|
|
180
|
+
notes: input.notes != null ? asString(input.notes) : null,
|
|
181
|
+
};
|
|
182
|
+
|
|
183
|
+
// Terminal output is unpermitted by default: dropped entirely, with a marker
|
|
184
|
+
// so the reader knows it was withheld (not merely absent).
|
|
185
|
+
if (includeTerminal && input.terminalOutput != null) {
|
|
186
|
+
bundle.terminalOutput = asString(input.terminalOutput);
|
|
187
|
+
} else {
|
|
188
|
+
bundle.terminalOutput = null;
|
|
189
|
+
bundle.terminalOutputWithheld = input.terminalOutput != null;
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
// Single de-identification pass over the WHOLE assembled bundle: every string
|
|
193
|
+
// field is secret-redacted regardless of which section it landed in, so the
|
|
194
|
+
// export gate is guaranteed clean no matter which signal carried a secret.
|
|
195
|
+
// env is already values-free, so its names (metadata) pass through untouched.
|
|
196
|
+
return deepRedact(bundle);
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
/**
|
|
200
|
+
* The mandatory pre-export gate ("导出前必须运行 Secret Scan"). Deep-scans the
|
|
201
|
+
* assembled bundle; fail-closed: if ANY residual secret survives, `ok` is false
|
|
202
|
+
* and `blocked` is true — the caller must NOT export it. On a clean bundle the
|
|
203
|
+
* gate returns it ready for export.
|
|
204
|
+
*
|
|
205
|
+
* @param {object} bundle a buildDiagnosticBundle() result
|
|
206
|
+
* @returns {{ok:boolean, blocked:boolean, findings:Array, bundle:object}}
|
|
207
|
+
*/
|
|
208
|
+
export function secretScanGate(bundle) {
|
|
209
|
+
const findings = scanBundleForSecrets(bundle);
|
|
210
|
+
const ok = findings.length === 0;
|
|
211
|
+
return { ok, blocked: !ok, findings, bundle };
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
/**
|
|
215
|
+
* One-call assembly + gate. Builds the de-identified bundle and runs the secret
|
|
216
|
+
* scan. When the gate is clean, returns the exportable bundle; when it is not
|
|
217
|
+
* (should never happen given assembly redacts, but defense-in-depth), returns
|
|
218
|
+
* `ok:false` and the leak locations, and does NOT hand back an exportable body.
|
|
219
|
+
*
|
|
220
|
+
* @returns {{ok:boolean, bundle:(object|null), findings:Array}}
|
|
221
|
+
*/
|
|
222
|
+
export function exportDiagnosticBundle(input = {}, opts = {}) {
|
|
223
|
+
const bundle = buildDiagnosticBundle(input, opts);
|
|
224
|
+
const gate = secretScanGate(bundle);
|
|
225
|
+
return {
|
|
226
|
+
ok: gate.ok,
|
|
227
|
+
bundle: gate.ok ? bundle : null,
|
|
228
|
+
findings: gate.findings,
|
|
229
|
+
};
|
|
230
|
+
}
|
|
@@ -23,10 +23,18 @@ import {
|
|
|
23
23
|
statSync,
|
|
24
24
|
rmSync,
|
|
25
25
|
} from "node:fs";
|
|
26
|
-
import { execSync } from "node:child_process";
|
|
26
|
+
import { execSync, spawnSync } from "node:child_process";
|
|
27
27
|
import { join, basename } from "node:path";
|
|
28
28
|
import { getHomeDir, getConfigPath } from "./paths.js";
|
|
29
|
-
import {
|
|
29
|
+
import {
|
|
30
|
+
checkAgenda,
|
|
31
|
+
checkInstructionFiles,
|
|
32
|
+
checkHookConfig,
|
|
33
|
+
checkHooks,
|
|
34
|
+
checkSandbox,
|
|
35
|
+
checkDuplicateSkills,
|
|
36
|
+
DEFAULT_CHECKUP_THRESHOLDS,
|
|
37
|
+
} from "./runtime-checkup.js";
|
|
30
38
|
|
|
31
39
|
export const CHECK_LEVELS = Object.freeze({
|
|
32
40
|
OK: "ok",
|
|
@@ -45,6 +53,7 @@ export const _deps = {
|
|
|
45
53
|
statSync,
|
|
46
54
|
rmSync,
|
|
47
55
|
execSync,
|
|
56
|
+
spawnSync,
|
|
48
57
|
now: () => Date.now(),
|
|
49
58
|
};
|
|
50
59
|
|
|
@@ -315,6 +324,58 @@ async function pluginSection(opts, _deps2) {
|
|
|
315
324
|
} catch (err) {
|
|
316
325
|
checks.push(failedCheck("plugins", "Installed plugins", err));
|
|
317
326
|
}
|
|
327
|
+
|
|
328
|
+
// Broken manifests + un-consented capabilities (Phase 3 capability gap).
|
|
329
|
+
// Distinct from the signature-lock check above: re-discover to get each
|
|
330
|
+
// plugin's parsed manifest (listInstalled drops it) so we can flag an invalid
|
|
331
|
+
// manifest and any DECLARED capability set the user has not consented to.
|
|
332
|
+
try {
|
|
333
|
+
const { discoverPlugins } = await import("./plugin-runtime/scopes.js");
|
|
334
|
+
const { isPluginCapabilityConsented } =
|
|
335
|
+
await import("./plugin-runtime/capability-consent.js");
|
|
336
|
+
const { checkPluginsAndLsp } = await import("./runtime-checkup.js");
|
|
337
|
+
const discovered =
|
|
338
|
+
discoverPlugins({ cwd: opts.cwd || process.cwd(), skipPolicy: true }) ||
|
|
339
|
+
[];
|
|
340
|
+
// Broken manifests → warn (shared runtime-checkup evaluator).
|
|
341
|
+
for (const f of checkPluginsAndLsp(
|
|
342
|
+
discovered.map((p) => ({
|
|
343
|
+
id: p.name,
|
|
344
|
+
healthy: p.manifest?.ok !== false,
|
|
345
|
+
kind: "plugin",
|
|
346
|
+
})),
|
|
347
|
+
)) {
|
|
348
|
+
checks.push(
|
|
349
|
+
check(
|
|
350
|
+
f.id,
|
|
351
|
+
`plugin ${f.ref}`,
|
|
352
|
+
CHECK_LEVELS.WARN,
|
|
353
|
+
`${f.message} — ${f.remediation}`,
|
|
354
|
+
),
|
|
355
|
+
);
|
|
356
|
+
}
|
|
357
|
+
// Declared capabilities not yet consented → warn.
|
|
358
|
+
for (const p of discovered) {
|
|
359
|
+
if (!p.manifest?.capabilitiesDeclared) continue;
|
|
360
|
+
const consented = isPluginCapabilityConsented(
|
|
361
|
+
{ name: p.name, scope: p.scope, version: p.version },
|
|
362
|
+
p.manifest.capabilities,
|
|
363
|
+
);
|
|
364
|
+
if (!consented) {
|
|
365
|
+
checks.push(
|
|
366
|
+
check(
|
|
367
|
+
"plugin-capability-unconsented",
|
|
368
|
+
`plugin ${p.name}`,
|
|
369
|
+
CHECK_LEVELS.WARN,
|
|
370
|
+
`declares capabilities not yet consented — run \`cc plugin consent ${p.name}\``,
|
|
371
|
+
),
|
|
372
|
+
);
|
|
373
|
+
}
|
|
374
|
+
}
|
|
375
|
+
} catch (err) {
|
|
376
|
+
checks.push(failedCheck("plugin-capabilities", "plugin capabilities", err));
|
|
377
|
+
}
|
|
378
|
+
|
|
318
379
|
return { id: "plugins", title: "Plugin trust", checks };
|
|
319
380
|
}
|
|
320
381
|
|
|
@@ -566,7 +627,7 @@ async function runtimeSection(opts, deps) {
|
|
|
566
627
|
dueAt: e.kind === "wakeup" ? e.dueAt : e.nextAt,
|
|
567
628
|
recurring: e.kind !== "wakeup",
|
|
568
629
|
}));
|
|
569
|
-
for (const f of checkAgenda(entries, now)) {
|
|
630
|
+
for (const f of checkAgenda(entries, now, DEFAULT_CHECKUP_THRESHOLDS)) {
|
|
570
631
|
checks.push(
|
|
571
632
|
check(
|
|
572
633
|
f.id,
|
|
@@ -595,7 +656,7 @@ async function runtimeSection(opts, deps) {
|
|
|
595
656
|
}
|
|
596
657
|
}
|
|
597
658
|
}
|
|
598
|
-
for (const f of checkInstructionFiles(files)) {
|
|
659
|
+
for (const f of checkInstructionFiles(files, DEFAULT_CHECKUP_THRESHOLDS)) {
|
|
599
660
|
checks.push(
|
|
600
661
|
check(
|
|
601
662
|
f.id,
|
|
@@ -609,12 +670,221 @@ async function runtimeSection(opts, deps) {
|
|
|
609
670
|
checks.push(failedCheck("instructions", "instruction files", err));
|
|
610
671
|
}
|
|
611
672
|
|
|
673
|
+
// settings.json hook config — statically catch hooks that will silently never
|
|
674
|
+
// fire (unknown event / bad regex matcher / missing command) or are
|
|
675
|
+
// misconfigured (bad timeout). Runtime hook health (slow/circuit-broken) needs
|
|
676
|
+
// live per-process stats and isn't computed here.
|
|
677
|
+
try {
|
|
678
|
+
const settingsHooks = await import("./settings-hooks.cjs");
|
|
679
|
+
const mod = settingsHooks.default || settingsHooks;
|
|
680
|
+
const cwd = opts.cwd || process.cwd();
|
|
681
|
+
const files = mod.settingsFiles(cwd);
|
|
682
|
+
const seen = new Set();
|
|
683
|
+
for (const file of files) {
|
|
684
|
+
if (seen.has(file) || !deps.existsSync(file)) continue;
|
|
685
|
+
seen.add(file);
|
|
686
|
+
let hooksBlock = null;
|
|
687
|
+
try {
|
|
688
|
+
const parsed = JSON.parse(deps.readFileSync(file, "utf-8"));
|
|
689
|
+
hooksBlock = parsed && typeof parsed === "object" ? parsed.hooks : null;
|
|
690
|
+
} catch {
|
|
691
|
+
continue; // malformed settings.json is reported elsewhere (config section)
|
|
692
|
+
}
|
|
693
|
+
if (!hooksBlock) continue;
|
|
694
|
+
for (const f of checkHookConfig(hooksBlock, {
|
|
695
|
+
validEvents: mod.HOOK_EVENTS,
|
|
696
|
+
})) {
|
|
697
|
+
checks.push(
|
|
698
|
+
check(
|
|
699
|
+
f.id,
|
|
700
|
+
`${basename(file)} ${f.ref}`,
|
|
701
|
+
SEVERITY_TO_LEVEL[f.severity] || CHECK_LEVELS.INFO,
|
|
702
|
+
`${f.message} — ${f.remediation}`,
|
|
703
|
+
),
|
|
704
|
+
);
|
|
705
|
+
}
|
|
706
|
+
}
|
|
707
|
+
} catch (err) {
|
|
708
|
+
checks.push(failedCheck("hook-config", "settings hook config", err));
|
|
709
|
+
}
|
|
710
|
+
|
|
711
|
+
// Async-hook RUNTIME health — the reliability aggregate persisted by
|
|
712
|
+
// AsyncHookSupervisor.stopAll(): hooks that have been repeatedly failing,
|
|
713
|
+
// circuit-broken (N failures in a row), or slow on average. Complements the
|
|
714
|
+
// static config check above (which catches hooks that never fire at all).
|
|
715
|
+
try {
|
|
716
|
+
const store = await import("./hook-stats-store.cjs");
|
|
717
|
+
const mod = store.default || store;
|
|
718
|
+
const statsFs = {
|
|
719
|
+
existsSync: deps.existsSync,
|
|
720
|
+
readFileSync: deps.readFileSync,
|
|
721
|
+
};
|
|
722
|
+
const stats = mod.loadHookStats(mod.defaultHookStatsPath(), statsFs);
|
|
723
|
+
const hooks = mod.toCheckHooksInput(stats, {
|
|
724
|
+
circuitThreshold: DEFAULT_CHECKUP_THRESHOLDS.hookFailureThreshold,
|
|
725
|
+
});
|
|
726
|
+
for (const f of checkHooks(hooks, DEFAULT_CHECKUP_THRESHOLDS)) {
|
|
727
|
+
checks.push(
|
|
728
|
+
check(
|
|
729
|
+
f.id,
|
|
730
|
+
`hook ${f.ref}`,
|
|
731
|
+
SEVERITY_TO_LEVEL[f.severity] || CHECK_LEVELS.INFO,
|
|
732
|
+
`${f.message} — ${f.remediation}`,
|
|
733
|
+
),
|
|
734
|
+
);
|
|
735
|
+
}
|
|
736
|
+
} catch (err) {
|
|
737
|
+
checks.push(failedCheck("hook-health", "async hook stats", err));
|
|
738
|
+
}
|
|
739
|
+
|
|
740
|
+
// Sandbox real capability — catch the "silent degradation" case where a
|
|
741
|
+
// sandbox is configured in settings.json but its engine (docker/bwrap) isn't
|
|
742
|
+
// actually runnable, so tool subprocesses run UNSANDBOXED without warning.
|
|
743
|
+
try {
|
|
744
|
+
const settingsHooks = await import("./settings-hooks.cjs");
|
|
745
|
+
const shMod = settingsHooks.default || settingsHooks;
|
|
746
|
+
const sandboxMod = await import("./agent-sandbox.js");
|
|
747
|
+
const cwd = opts.cwd || process.cwd();
|
|
748
|
+
let sandboxSettings = null;
|
|
749
|
+
for (const file of shMod.settingsFiles(cwd)) {
|
|
750
|
+
if (!deps.existsSync(file)) continue;
|
|
751
|
+
try {
|
|
752
|
+
const parsed = JSON.parse(deps.readFileSync(file, "utf-8"));
|
|
753
|
+
if (parsed && parsed.sandbox && typeof parsed.sandbox === "object") {
|
|
754
|
+
sandboxSettings = { ...(sandboxSettings || {}), ...parsed.sandbox };
|
|
755
|
+
}
|
|
756
|
+
} catch {
|
|
757
|
+
// malformed settings.json is reported by the config section
|
|
758
|
+
}
|
|
759
|
+
}
|
|
760
|
+
if (sandboxSettings && sandboxSettings.enabled === true) {
|
|
761
|
+
const sandbox = sandboxMod.normalizeAgentSandbox(true, {
|
|
762
|
+
settings: sandboxSettings,
|
|
763
|
+
cwd,
|
|
764
|
+
});
|
|
765
|
+
if (sandbox) {
|
|
766
|
+
const probe = sandboxMod.probeSandboxAvailability(sandbox, {
|
|
767
|
+
spawnSync: deps.spawnSync,
|
|
768
|
+
});
|
|
769
|
+
for (const f of checkSandbox({
|
|
770
|
+
configured: true,
|
|
771
|
+
engine: sandbox.engine,
|
|
772
|
+
available: probe.available,
|
|
773
|
+
reason: probe.reason,
|
|
774
|
+
failIfUnavailable: sandbox.policy?.failIfUnavailable === true,
|
|
775
|
+
isolationLevel: sandboxMod.isolationLevel(sandbox),
|
|
776
|
+
})) {
|
|
777
|
+
checks.push(
|
|
778
|
+
check(
|
|
779
|
+
f.id,
|
|
780
|
+
`sandbox ${f.ref}`,
|
|
781
|
+
SEVERITY_TO_LEVEL[f.severity] || CHECK_LEVELS.INFO,
|
|
782
|
+
f.remediation ? `${f.message} — ${f.remediation}` : f.message,
|
|
783
|
+
),
|
|
784
|
+
);
|
|
785
|
+
}
|
|
786
|
+
}
|
|
787
|
+
}
|
|
788
|
+
} catch (err) {
|
|
789
|
+
checks.push(failedCheck("sandbox", "sandbox availability", err));
|
|
790
|
+
}
|
|
791
|
+
|
|
792
|
+
// Duplicate skills — an id defined in more than one layer silently shadows
|
|
793
|
+
// (loadAll keeps only the winner), so a user's custom skill can override, or
|
|
794
|
+
// be overridden by, another without any signal. opts.skillLayerEntries lets a
|
|
795
|
+
// caller inject the (un-deduped) entry list; otherwise scan via the loader.
|
|
796
|
+
try {
|
|
797
|
+
let entries = opts.skillLayerEntries;
|
|
798
|
+
if (!entries) {
|
|
799
|
+
const { CLISkillLoader } = await import("./skill-loader.js");
|
|
800
|
+
entries = new CLISkillLoader().listSkillLayerEntries();
|
|
801
|
+
}
|
|
802
|
+
for (const f of checkDuplicateSkills(entries)) {
|
|
803
|
+
checks.push(
|
|
804
|
+
check(
|
|
805
|
+
f.id,
|
|
806
|
+
`skill ${f.ref}`,
|
|
807
|
+
SEVERITY_TO_LEVEL[f.severity] || CHECK_LEVELS.INFO,
|
|
808
|
+
`${f.message} — ${f.remediation}`,
|
|
809
|
+
),
|
|
810
|
+
);
|
|
811
|
+
}
|
|
812
|
+
} catch (err) {
|
|
813
|
+
checks.push(failedCheck("skill-duplicate", "skill layers", err));
|
|
814
|
+
}
|
|
815
|
+
|
|
612
816
|
if (checks.length === 0) {
|
|
613
817
|
checks.push(check("runtime-clean", "runtime hygiene", CHECK_LEVELS.OK, ""));
|
|
614
818
|
}
|
|
615
819
|
return { id: "runtime", title: "Runtime checkup", checks };
|
|
616
820
|
}
|
|
617
821
|
|
|
822
|
+
// ── execution context (P1-7) ───────────────────────────────────────────────
|
|
823
|
+
// "任务在哪执行是 Session 一等属性": surface the DETECTED execution location and
|
|
824
|
+
// the fail-closed permission posture it implies. Read-only advisory — it never
|
|
825
|
+
// blocks. A local trusted machine is OK; a remote/unknown environment is a WARN
|
|
826
|
+
// because a session there fails closed to read-only unless powers are granted
|
|
827
|
+
// explicitly (and any policy violation from the pure model is listed).
|
|
828
|
+
async function executionSection(opts, deps) {
|
|
829
|
+
const checks = [];
|
|
830
|
+
try {
|
|
831
|
+
const {
|
|
832
|
+
detectAmbientLocation,
|
|
833
|
+
validateExecutionContext,
|
|
834
|
+
EXECUTION_LOCATION,
|
|
835
|
+
} = await import("./execution-location.js");
|
|
836
|
+
// /.dockerenv is the classic in-container marker; keep it injectable via deps
|
|
837
|
+
// so the section is deterministic under test.
|
|
838
|
+
let dockerEnvFileExists = false;
|
|
839
|
+
try {
|
|
840
|
+
dockerEnvFileExists = deps.existsSync("/.dockerenv") === true;
|
|
841
|
+
} catch {
|
|
842
|
+
// non-POSIX host / unreadable — treat as not-a-container.
|
|
843
|
+
}
|
|
844
|
+
const location = detectAmbientLocation({
|
|
845
|
+
env: opts.env || process.env,
|
|
846
|
+
dockerEnvFileExists,
|
|
847
|
+
});
|
|
848
|
+
// Honest minimal input: just the detected location + the working dir. We do
|
|
849
|
+
// NOT fabricate a return path or granted powers — the validation then flags a
|
|
850
|
+
// real remote box with nowhere to return results, which is a meaningful hint.
|
|
851
|
+
const result = validateExecutionContext({
|
|
852
|
+
location,
|
|
853
|
+
source: { dir: opts.cwd || process.cwd() },
|
|
854
|
+
});
|
|
855
|
+
const isUnknown = location === EXECUTION_LOCATION.UNKNOWN;
|
|
856
|
+
const level =
|
|
857
|
+
isUnknown || result.descriptor.remote
|
|
858
|
+
? CHECK_LEVELS.WARN
|
|
859
|
+
: CHECK_LEVELS.OK;
|
|
860
|
+
checks.push(
|
|
861
|
+
check(
|
|
862
|
+
"execution-location",
|
|
863
|
+
`Execution location: ${location}`,
|
|
864
|
+
level,
|
|
865
|
+
result.descriptor.remote
|
|
866
|
+
? "remote/unknown — a session here fails closed to read-only unless powers are granted explicitly"
|
|
867
|
+
: isUnknown
|
|
868
|
+
? "could not identify the environment — treated as untrusted (read-only floor)"
|
|
869
|
+
: "local trusted machine",
|
|
870
|
+
),
|
|
871
|
+
);
|
|
872
|
+
if (result.violations.length > 0) {
|
|
873
|
+
checks.push(
|
|
874
|
+
check(
|
|
875
|
+
"execution-policy",
|
|
876
|
+
"Execution policy",
|
|
877
|
+
CHECK_LEVELS.WARN,
|
|
878
|
+
`fail-closed advisories: ${result.violations.join(", ")}`,
|
|
879
|
+
),
|
|
880
|
+
);
|
|
881
|
+
}
|
|
882
|
+
} catch (err) {
|
|
883
|
+
checks.push(failedCheck("execution-location", "execution context", err));
|
|
884
|
+
}
|
|
885
|
+
return { id: "execution", title: "Execution context", checks };
|
|
886
|
+
}
|
|
887
|
+
|
|
618
888
|
/**
|
|
619
889
|
* Collect all checkup sections. Never throws — a failing subsystem becomes an
|
|
620
890
|
* `err` check inside its section.
|
|
@@ -633,6 +903,7 @@ export async function collectCheckupSections(opts = {}) {
|
|
|
633
903
|
backgroundSection,
|
|
634
904
|
worktreeSection,
|
|
635
905
|
runtimeSection,
|
|
906
|
+
executionSection,
|
|
636
907
|
]) {
|
|
637
908
|
try {
|
|
638
909
|
sections.push(await build(opts, deps));
|