chainlesschain 0.162.176 → 0.162.177
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/assets/{AIOps-BDLu5Elz.js → AIOps-DT-VPWuN.js} +1 -1
- package/src/assets/web-panel/assets/{ActionButton-DH5us-CK.js → ActionButton-DyOreOVN.js} +1 -1
- package/src/assets/web-panel/assets/{Analytics-BZ0u8IFG.js → Analytics-DslNsKux.js} +3 -3
- package/src/assets/web-panel/assets/{AppLayout-Duu8xR14.js → AppLayout-BNJJAYME.js} +5 -5
- package/src/assets/web-panel/assets/{Artifacts-Br2YBbh_.js → Artifacts-DoaAtEcY.js} +1 -1
- package/src/assets/web-panel/assets/{Audit-C3C6a482.js → Audit-DPG3WdJ9.js} +1 -1
- package/src/assets/web-panel/assets/{BackgroundAgents-CzHvebcj.js → BackgroundAgents-C_JO6R5G.js} +1 -1
- package/src/assets/web-panel/assets/{Backup-DZNHYDFd.js → Backup-Cv3BooyX.js} +1 -1
- package/src/assets/web-panel/assets/{BaseInput-VRiC4fIx.js → BaseInput-D2uQXWu-.js} +1 -1
- package/src/assets/web-panel/assets/{Chat-Cm08WWAD.js → Chat-B4Vczyf3.js} +4 -4
- package/src/assets/web-panel/assets/ChatBubbleRenderer-DE6reP99.js +1 -0
- package/src/assets/web-panel/assets/{Checkbox-DtByk7d7.js → Checkbox-qTgk4VeP.js} +1 -1
- package/src/assets/web-panel/assets/{Codegen-CphXXOfD.js → Codegen-DlLWcIzj.js} +1 -1
- package/src/assets/web-panel/assets/{Col-Hz86TnLR.js → Col-DNO73qp7.js} +1 -1
- package/src/assets/web-panel/assets/{Community-HP03ltpD.js → Community-vRDNpc4j.js} +1 -1
- package/src/assets/web-panel/assets/{Compact-B_N1mqQz.js → Compact-CEWtIXni.js} +1 -1
- package/src/assets/web-panel/assets/{Compliance-CVztRpi8.js → Compliance-KgYC9Fxi.js} +1 -1
- package/src/assets/web-panel/assets/{Cowork-wUVOyZih.js → Cowork-zj5wvkpT.js} +4 -4
- package/src/assets/web-panel/assets/{Cron-DW5TLJKw.js → Cron-BLDoCozU.js} +2 -2
- package/src/assets/web-panel/assets/{Crosschain-CrAFGhWH.js → Crosschain-BYnff4VY.js} +1 -1
- package/src/assets/web-panel/assets/{DID-pA-oD8ke.js → DID-C75VFsVm.js} +2 -2
- package/src/assets/web-panel/assets/{Dashboard-CXrd5gAw.js → Dashboard-C4Gutni2.js} +2 -2
- package/src/assets/web-panel/assets/{Dropdown-EweJcsek.js → Dropdown-B5dbO4Kx.js} +1 -1
- package/src/assets/web-panel/assets/{EmailListRenderer-C9xVLPG4.js → EmailListRenderer-BDipZtHJ.js} +1 -1
- package/src/assets/web-panel/assets/{FamilyGuardDashboard-CDcEEqlu.js → FamilyGuardDashboard-DmIzt8jj.js} +1 -1
- package/src/assets/web-panel/assets/{Federation-DtZda9A_.js → Federation-D9QOS-uW.js} +1 -1
- package/src/assets/web-panel/assets/{FormItemContext-CiYdcVz3.js → FormItemContext-B47HRwAm.js} +1 -1
- package/src/assets/web-panel/assets/{GenericCardRenderer-DA13uwA0.js → GenericCardRenderer-3R4mXmgV.js} +1 -1
- package/src/assets/web-panel/assets/{Git-DzRhZD1O.js → Git-DrNXZf1C.js} +2 -2
- package/src/assets/web-panel/assets/{Governance-D5RMgOSb.js → Governance-BGd7JR98.js} +1 -1
- package/src/assets/web-panel/assets/{Inference-BgrPfSNk.js → Inference-D-2he1ed.js} +1 -1
- package/src/assets/web-panel/assets/{KnowledgeGraph-DzjnD571.js → KnowledgeGraph-C0sHIbK5.js} +1 -1
- package/src/assets/web-panel/assets/{Logs-BhWzVDtN.js → Logs-DiBQO-jL.js} +2 -2
- package/src/assets/web-panel/assets/{MarkdownRenderer-BDape9cx.js → MarkdownRenderer-DL_5eV2D.js} +1 -1
- package/src/assets/web-panel/assets/{Marketplace-DJtL5GV7.js → Marketplace-VR59OdrC.js} +1 -1
- package/src/assets/web-panel/assets/{McpTools-CZcQygI7.js → McpTools-DrPzwSd3.js} +5 -5
- package/src/assets/web-panel/assets/{Memory-DUWSDUUu.js → Memory-CBgKALwQ.js} +2 -2
- package/src/assets/web-panel/assets/{MobileBridge-BTwq2WKY.js → MobileBridge-pQ9Tn-Mn.js} +1 -1
- package/src/assets/web-panel/assets/MobileProjects-mASB6jyj.js +1 -0
- package/src/assets/web-panel/assets/{Mtc-BiGugEQm.js → Mtc-DTMgE7HC.js} +5 -5
- package/src/assets/web-panel/assets/{MtcAudit-DY7a6gUe.js → MtcAudit-CwTnCtiL.js} +2 -2
- package/src/assets/web-panel/assets/{Multisig-BQbDu_w7.js → Multisig-ngm6WU8f.js} +3 -3
- package/src/assets/web-panel/assets/{NLProgramming-CM2wXTLv.js → NLProgramming-4YO4Qww9.js} +1 -1
- package/src/assets/web-panel/assets/{Notes-DI6gGs1v.js → Notes-DP1k45mF.js} +4 -4
- package/src/assets/web-panel/assets/{NotificationSettings-BsZOLWAB.js → NotificationSettings-r2H8yPKb.js} +1 -1
- package/src/assets/web-panel/assets/{OrderTableRenderer-C48mu09U.js → OrderTableRenderer-CoM_Zw0J.js} +1 -1
- package/src/assets/web-panel/assets/{Organization-Cds0_tXr.js → Organization-DrO7jRWS.js} +4 -4
- package/src/assets/web-panel/assets/{Overflow-WunYMD87.js → Overflow-Cnwu4i4t.js} +1 -1
- package/src/assets/web-panel/assets/{P2P-Bo6j4DDv.js → P2P-zi-cDwA_.js} +2 -2
- package/src/assets/web-panel/assets/{PdhVaultBrowser-Bh2XPD5f.js → PdhVaultBrowser-DBEryDv7.js} +3 -3
- package/src/assets/web-panel/assets/{Permissions-EMvMMC8N.js → Permissions-lqXK2C3x.js} +4 -4
- package/src/assets/web-panel/assets/{PersonalDataHub-AX3ypUDV.js → PersonalDataHub-b63T3HJM.js} +2 -2
- package/src/assets/web-panel/assets/{Pipeline-BOx8HOgL.js → Pipeline-Kep5KAGW.js} +1 -1
- package/src/assets/web-panel/assets/{Privacy-tBeTVQVM.js → Privacy-CHdvOAh1.js} +1 -1
- package/src/assets/web-panel/assets/{ProjectInit-CVYg1W4M.js → ProjectInit-BhKfdwe9.js} +2 -2
- package/src/assets/web-panel/assets/{ProjectSettings-Dvt5I7y1.js → ProjectSettings-Yavk9K8A.js} +2 -2
- package/src/assets/web-panel/assets/{Projects-Cc7FC9-e.js → Projects-CN8E_5Mq.js} +1 -1
- package/src/assets/web-panel/assets/{Providers-Ct8xqY-E.js → Providers-Bfym6SQn.js} +1 -1
- package/src/assets/web-panel/assets/{QrScannerModal-Cni8Ooja.js → QrScannerModal-CLSQcrlR.js} +1 -1
- package/src/assets/web-panel/assets/{QuickAsk-BJVLKdxr.js → QuickAsk-VPa2WKQN.js} +1 -1
- package/src/assets/web-panel/assets/{Recommend-DPG8Rbbm.js → Recommend-CGXR8jVU.js} +1 -1
- package/src/assets/web-panel/assets/{RemoteSession-DsdR7dCt.js → RemoteSession-C_20YfMB.js} +2 -2
- package/src/assets/web-panel/assets/{Reputation-Csg1YM-b.js → Reputation-awZh68UV.js} +1 -1
- package/src/assets/web-panel/assets/{Row-D9DoFoEf.js → Row-Ctb4oiYN.js} +1 -1
- package/src/assets/web-panel/assets/{RssFeed-BsvKrXMS.js → RssFeed-D3sdjvUV.js} +2 -2
- package/src/assets/web-panel/assets/{Search-DFUStfgQ.js → Search-C-zfA4kT.js} +1 -1
- package/src/assets/web-panel/assets/{Security-ILg1zxdK.js → Security-D9XlmHA3.js} +4 -4
- package/src/assets/web-panel/assets/{Services-Dd41PCaY.js → Services-C3UKnr41.js} +2 -2
- package/src/assets/web-panel/assets/{Skeleton-Bzo1cN80.js → Skeleton-BQxagZfF.js} +1 -1
- package/src/assets/web-panel/assets/Skills-q6eb6KPZ.js +1 -0
- package/src/assets/web-panel/assets/{Sla-jl7UTiRY.js → Sla-DveENvek.js} +1 -1
- package/src/assets/web-panel/assets/{SpeechSettings-BePa-RF6.js → SpeechSettings-BFjXLNgF.js} +1 -1
- package/src/assets/web-panel/assets/{SyncSettings-DiqqDKGZ.js → SyncSettings-BpHgbaP9.js} +2 -2
- package/src/assets/web-panel/assets/Tasks-DvepRb8-.js +1 -0
- package/src/assets/web-panel/assets/{Templates-JiDeI06L.js → Templates-DEiaoyve.js} +1 -1
- package/src/assets/web-panel/assets/{Tenant-hhgI6zwS.js → Tenant-ITg774nY.js} +1 -1
- package/src/assets/web-panel/assets/Terminal-D3ZBcvnb.js +3 -0
- package/src/assets/web-panel/assets/{TimelineRenderer-B8uB5Llk.js → TimelineRenderer-BsLU6JjU.js} +1 -1
- package/src/assets/web-panel/assets/{Tokens-C3b_6r2z.js → Tokens-Cb5Pquc9.js} +1 -1
- package/src/assets/web-panel/assets/{Trigger-tIP7HKVe.js → Trigger-Dwfy-m9N.js} +1 -1
- package/src/assets/web-panel/assets/{Trust-vjoc11vJ.js → Trust-CnlSRGG0.js} +1 -1
- package/src/assets/web-panel/assets/{UkeySign-DUPBnGuH.js → UkeySign-Y4TlvKVo.js} +1 -1
- package/src/assets/web-panel/assets/{VideoEditing-CBvhUY5T.js → VideoEditing-BpKLtTTO.js} +1 -1
- package/src/assets/web-panel/assets/{Wallet-DUaQ0uA5.js → Wallet-LLBzIq8x.js} +4 -4
- package/src/assets/web-panel/assets/{WebAuthn-DllSivjE.js → WebAuthn-BeSRiusP.js} +5 -5
- package/src/assets/web-panel/assets/{WorkflowEditor-xc8D3uO6.js → WorkflowEditor-BK_l1tXO.js} +1 -1
- package/src/assets/web-panel/assets/{chat-BVicU7oA.js → chat-DYd-X7Wg.js} +1 -1
- package/src/assets/web-panel/assets/{colors-DJzLmhLm.js → colors-C9uip71j.js} +1 -1
- package/src/assets/web-panel/assets/{compact-item-BvYRfThE.js → compact-item-JBrfjUP6.js} +1 -1
- package/src/assets/web-panel/assets/{createContext-DGXxCBKR.js → createContext-D03fbuiq.js} +1 -1
- package/src/assets/web-panel/assets/devWarning-B8LQ8leS.js +1 -0
- package/src/assets/web-panel/assets/{hasIn-SBDJB1y4.js → hasIn-DLQOR6l-.js} +1 -1
- package/src/assets/web-panel/assets/{index-MjC4G09d.js → index-5YN7F5bO.js} +1 -1
- package/src/assets/web-panel/assets/{index-Dm_77jgR.js → index-6GuLRpBQ.js} +1 -1
- package/src/assets/web-panel/assets/{index-CtwqO5qo.js → index-B0X76LjX.js} +1 -1
- package/src/assets/web-panel/assets/{index-DRfiXB_p.js → index-B46YcgsV.js} +1 -1
- package/src/assets/web-panel/assets/{index-VMfwIH5G.js → index-BNCdRmWf.js} +1 -1
- package/src/assets/web-panel/assets/{index-hxTODbM1.js → index-BYv-LWdy.js} +1 -1
- package/src/assets/web-panel/assets/{index-DMarmfrv.js → index-Ba-Ps_AJ.js} +1 -1
- package/src/assets/web-panel/assets/{index-xfluRkCT.js → index-BcBZwlnG.js} +1 -1
- package/src/assets/web-panel/assets/{index-BYqz4Wrr.js → index-BdH9l8wl.js} +1 -1
- package/src/assets/web-panel/assets/{index-BO1itZaY.js → index-BfOs_bmg.js} +3 -3
- package/src/assets/web-panel/assets/index-Bh8BPtqR.js +1 -0
- package/src/assets/web-panel/assets/{index-Cf-O_g_8.js → index-BnQgRjsm.js} +1 -1
- package/src/assets/web-panel/assets/{index-CKyEivf8.js → index-BpYKLrUT.js} +1 -1
- package/src/assets/web-panel/assets/{index-CoJAO1Lm.js → index-BuNFCZCx.js} +1 -1
- package/src/assets/web-panel/assets/{index-1SdDS6lR.js → index-ByHO3SqZ.js} +1 -1
- package/src/assets/web-panel/assets/{index-DnjPdvR6.js → index-C2zmOsyD.js} +1 -1
- package/src/assets/web-panel/assets/{index-Bf-ctERC.js → index-CChQBM1d.js} +1 -1
- package/src/assets/web-panel/assets/{index-DCdRAgjy.js → index-CChaISMb.js} +1 -1
- package/src/assets/web-panel/assets/{index-Crk5NTb0.js → index-CILVyK3b.js} +1 -1
- package/src/assets/web-panel/assets/{index-9gdI7uUW.js → index-CL0ixCxk.js} +1 -1
- package/src/assets/web-panel/assets/{index-BJZf9DhV.js → index-CS63r8h3.js} +1 -1
- package/src/assets/web-panel/assets/{index-DCqRS6da.js → index-CVZWiUE_.js} +1 -1
- package/src/assets/web-panel/assets/{index-DonCnfMr.js → index-Ce0Bf1YF.js} +1 -1
- package/src/assets/web-panel/assets/index-CeVrHLzZ.js +1 -0
- package/src/assets/web-panel/assets/{index-CloCgBUR.js → index-Ck17i0OD.js} +1 -1
- package/src/assets/web-panel/assets/{index-wcCL-miV.js → index-Cpn19c9k.js} +1 -1
- package/src/assets/web-panel/assets/{index-BTrBGBJs.js → index-CqWk1FLG.js} +1 -1
- package/src/assets/web-panel/assets/{index-e4fXH_WO.js → index-CwoDpKzW.js} +1 -1
- package/src/assets/web-panel/assets/{index-DvUTDYXX.js → index-D-1SqEpQ.js} +1 -1
- package/src/assets/web-panel/assets/{index-CdGY9P8a.js → index-D4UY5Z60.js} +1 -1
- package/src/assets/web-panel/assets/{index-DKrvvWm4.js → index-D8KvuDz0.js} +1 -1
- package/src/assets/web-panel/assets/{index-lpaGoTH3.js → index-DNMAAUVk.js} +1 -1
- package/src/assets/web-panel/assets/{index-YJkzy8ZF.js → index-DYLZY4uh.js} +1 -1
- package/src/assets/web-panel/assets/{index-FxZzRyE1.js → index-DaokIHt3.js} +1 -1
- package/src/assets/web-panel/assets/{index-DHVLJ8q0.js → index-Q7mmL6w4.js} +1 -1
- package/src/assets/web-panel/assets/{index-Cs2rmLFt.js → index-SO6lgMg0.js} +1 -1
- package/src/assets/web-panel/assets/{index-z1ZylsLq.js → index-iahNhZwm.js} +1 -1
- package/src/assets/web-panel/assets/{index-d5BVd-cL.js → index-l08afIbV.js} +1 -1
- package/src/assets/web-panel/assets/{index-D7HimK0M.js → index-rIxxfdTN.js} +1 -1
- package/src/assets/web-panel/assets/{initDefaultProps-C1zO1KnY.js → initDefaultProps-CG9z1c6w.js} +1 -1
- package/src/assets/web-panel/assets/{motion-CWLtEGmC.js → motion-B1Ky7Zff.js} +1 -1
- package/src/assets/web-panel/assets/{move-DzD_C3Vm.js → move-DME8X2P1.js} +1 -1
- package/src/assets/web-panel/assets/{mtc-parser-D880PYJl.js → mtc-parser-cuJYSWB7.js} +1 -1
- package/src/assets/web-panel/assets/{omit-CFR_z2ov.js → omit-UI_EE0uZ.js} +1 -1
- package/src/assets/web-panel/assets/{pickAttrs-srIhgrSC.js → pickAttrs-BIoE_Z_V.js} +1 -1
- package/src/assets/web-panel/assets/{placementArrow-C1rXpibP.js → placementArrow-B48-9HKN.js} +1 -1
- package/src/assets/web-panel/assets/{responsiveObserve-C30gRcSS.js → responsiveObserve-D7x_GDgD.js} +1 -1
- package/src/assets/web-panel/assets/{slide-CvtKiMfV.js → slide-DjzC5_7m.js} +1 -1
- package/src/assets/web-panel/assets/{statusUtils-DNHO7ITG.js → statusUtils-C6BIymhh.js} +1 -1
- package/src/assets/web-panel/assets/{styleChecker-Dt_Sl-g4.js → styleChecker-B4dGrdD9.js} +1 -1
- package/src/assets/web-panel/assets/{useFlexGapSupport-Cjev42Gh.js → useFlexGapSupport-Bken4aNE.js} +1 -1
- package/src/assets/web-panel/assets/{useFs-Baxglj2V.js → useFs-BMlqECqv.js} +1 -1
- package/src/assets/web-panel/assets/{usePersonalDataHub-B4NkcLaP.js → usePersonalDataHub-DLdrfmMa.js} +1 -1
- package/src/assets/web-panel/assets/{vnode-mIRkdT7o.js → vnode-CKJQmxa0.js} +1 -1
- package/src/assets/web-panel/assets/{zoom-HzI5DlH_.js → zoom-CaZunuau.js} +1 -1
- package/src/assets/web-panel/index.html +1 -1
- package/src/commands/agenda.js +22 -6
- package/src/commands/agent.js +3 -3
- package/src/commands/background-session.js +11 -4
- package/src/commands/batch.js +79 -27
- package/src/commands/changelog.js +15 -4
- package/src/commands/eval.js +21 -6
- package/src/commands/hook.js +2 -2
- package/src/commands/loop.js +9 -2
- package/src/commands/skill.js +2 -0
- package/src/commands/statusline.js +3 -3
- package/src/data/changelog.json +293 -51
- package/src/harness/background-task-manager.js +18 -6
- package/src/harness/background-task-worker.js +11 -18
- package/src/harness/mcp-client.js +9 -15
- package/src/harness/worktree-isolator.js +18 -21
- package/src/lib/agent-core.js +5 -0
- package/src/lib/agent-ipc-bus.js +12 -3
- package/src/lib/agent-team/team-worktree.js +28 -6
- package/src/lib/agent-worktree.js +20 -19
- package/src/lib/async-hook-supervisor.cjs +134 -24
- package/src/lib/async-hook-supervisor.js +47 -0
- package/src/lib/background-agent-supervisor.js +35 -12
- package/src/lib/background-task-manager.js +5 -1
- package/src/lib/changelog.js +57 -0
- package/src/lib/chrome-connector.js +6 -2
- package/src/lib/claude-code-bridge.js +24 -8
- package/src/lib/cli-anything-bridge.js +82 -25
- package/src/lib/doctor-checkup.js +18 -6
- package/src/lib/eval/tasks.js +25 -31
- package/src/lib/hook-runner.cjs +71 -36
- package/src/lib/hook-runner.js +55 -0
- package/src/lib/host-adb-bridge.js +18 -3
- package/src/lib/lsp/__tests__/benchmark.test.js +61 -0
- package/src/lib/lsp/__tests__/lsp-client.test.js +6 -1
- package/src/lib/lsp/benchmark.js +20 -4
- package/src/lib/lsp/lsp-client.js +6 -4
- package/src/lib/lsp/lsp-server-registry.js +0 -2
- package/src/lib/orchestrator.js +8 -2
- package/src/lib/packer/pack-update-applier.js +14 -2
- package/src/lib/packer/smoke-runner.js +18 -5
- package/src/lib/paths.js +1 -1
- package/src/lib/plugin-monitor-supervisor.js +20 -13
- package/src/lib/plugin-runtime/install.js +10 -3
- package/src/lib/repl-bang-memorize.js +10 -2
- package/src/lib/repl-goal.js +19 -2
- package/src/lib/settings-hook-events.cjs +13 -8
- package/src/lib/settings-hook-events.js +20 -0
- package/src/lib/skill-packs/generator.js +18 -9
- package/src/lib/skill-process-broker.js +71 -0
- package/src/lib/slash-commands.js +10 -2
- package/src/lib/status-line.cjs +28 -7
- package/src/lib/status-line.js +33 -0
- package/src/repl/agent-repl.js +48 -43
- package/src/runtime/agent-core.js +133 -49
- package/src/runtime/headless-runner.js +25 -10
- package/src/runtime/headless-stream.js +74 -4
- package/src/runtime/session-slash-commands.js +506 -0
- package/src/skills/video-editing/extractors/audio-extractor.js +13 -5
- package/src/skills/video-editing/extractors/video-extractor.js +13 -3
- package/src/skills/video-editing/media-process.js +20 -0
- package/src/skills/video-editing/render/audio-mix.js +13 -3
- package/src/skills/video-editing/render/ffmpeg-concat.js +7 -2
- package/src/skills/video-editing/render/ffmpeg-extract.js +7 -2
- package/src/workers/background-agent-worker.js +6 -2
- package/src/assets/web-panel/assets/ChatBubbleRenderer-CGHYrugQ.js +0 -1
- package/src/assets/web-panel/assets/MobileProjects-NZvvfDME.js +0 -1
- package/src/assets/web-panel/assets/Skills-BDHh17P0.js +0 -1
- package/src/assets/web-panel/assets/Tasks-DYQ_6lxj.js +0 -1
- package/src/assets/web-panel/assets/Terminal-CjTZPa37.js +0 -3
- package/src/assets/web-panel/assets/devWarning-BT46Y_QI.js +0 -1
- package/src/assets/web-panel/assets/index-B7nuIywP.js +0 -1
- package/src/assets/web-panel/assets/index-ycwAfC-i.js +0 -1
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
* Sends messages to parent: { type: "heartbeat"|"result"|"error", ... }
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
|
-
import
|
|
8
|
+
import executionBroker from "../lib/process-execution-broker/index.js";
|
|
9
9
|
|
|
10
10
|
const [command, cwd, type] = process.argv.slice(2);
|
|
11
11
|
|
|
@@ -14,23 +14,16 @@ const heartbeat = setInterval(() => {
|
|
|
14
14
|
}, 5000);
|
|
15
15
|
|
|
16
16
|
try {
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
}
|
|
27
|
-
result = execSync(command, {
|
|
28
|
-
cwd: cwd || process.cwd(),
|
|
29
|
-
encoding: "utf-8",
|
|
30
|
-
timeout: 300000,
|
|
31
|
-
maxBuffer: 10 * 1024 * 1024,
|
|
32
|
-
});
|
|
33
|
-
}
|
|
17
|
+
const result = executionBroker.execSync(command, {
|
|
18
|
+
cwd: cwd || process.cwd(),
|
|
19
|
+
encoding: "utf-8",
|
|
20
|
+
timeout: 300000,
|
|
21
|
+
maxBuffer: 10 * 1024 * 1024,
|
|
22
|
+
origin: `background-task:command:${type || "unknown"}`,
|
|
23
|
+
policy: "allow",
|
|
24
|
+
scope: "background-task",
|
|
25
|
+
shell: true,
|
|
26
|
+
});
|
|
34
27
|
|
|
35
28
|
if (process.send) {
|
|
36
29
|
process.send({ type: "result", data: result || "Done" });
|
|
@@ -8,7 +8,6 @@
|
|
|
8
8
|
* thin re-export shim for backwards compatibility.
|
|
9
9
|
*/
|
|
10
10
|
|
|
11
|
-
import { spawn } from "child_process";
|
|
12
11
|
import { executionBroker } from "../lib/process-execution-broker/index.js";
|
|
13
12
|
import { AsyncLocalStorage } from "node:async_hooks";
|
|
14
13
|
import { EventEmitter } from "events";
|
|
@@ -21,7 +20,7 @@ import { EventRuntimeProducer } from "../lib/event-runtime-producer.js";
|
|
|
21
20
|
* `fetch` defaults to the global fetch (Node 18+).
|
|
22
21
|
*/
|
|
23
22
|
export const _deps = {
|
|
24
|
-
spawn,
|
|
23
|
+
spawn: executionBroker.spawn.bind(executionBroker),
|
|
25
24
|
fetch: (...args) => globalThis.fetch(...args),
|
|
26
25
|
// Backoff sleep seam (tests override with a no-op so retries don't wait).
|
|
27
26
|
sleep: (ms) => new Promise((resolve) => setTimeout(resolve, ms)),
|
|
@@ -486,10 +485,7 @@ export class MCPClient extends EventEmitter {
|
|
|
486
485
|
`stdio transport requires a command (server "${name}")`,
|
|
487
486
|
);
|
|
488
487
|
}
|
|
489
|
-
const
|
|
490
|
-
? executionBroker.spawn.bind(executionBroker)
|
|
491
|
-
: _deps.spawn;
|
|
492
|
-
const proc = spawnFn(config.command, config.args || [], {
|
|
488
|
+
const proc = _deps.spawn(config.command, config.args || [], {
|
|
493
489
|
stdio: ["pipe", "pipe", "pipe"],
|
|
494
490
|
// process.env < agent identity (CLAUDECODE / session id) < the
|
|
495
491
|
// server's own config.env, so an explicit per-server override wins.
|
|
@@ -498,15 +494,13 @@ export class MCPClient extends EventEmitter {
|
|
|
498
494
|
...this._agentIdentityEnv(),
|
|
499
495
|
...(config.env || {}),
|
|
500
496
|
},
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
}
|
|
509
|
-
: {}),
|
|
497
|
+
origin: config.origin || `mcp:server:${name}`,
|
|
498
|
+
policy: config.policy || "allow",
|
|
499
|
+
scope: config.scope || "mcp",
|
|
500
|
+
shell: false,
|
|
501
|
+
pluginId: config.pluginId,
|
|
502
|
+
pluginVersion: config.pluginVersion,
|
|
503
|
+
pluginSource: config.pluginSource,
|
|
510
504
|
});
|
|
511
505
|
|
|
512
506
|
entry.process = proc;
|
|
@@ -7,7 +7,6 @@
|
|
|
7
7
|
* Feature-flag gated: WORKTREE_ISOLATION
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
|
-
import { execSync } from "node:child_process";
|
|
11
10
|
import { existsSync, rmSync, symlinkSync, mkdirSync } from "node:fs";
|
|
12
11
|
import { resolve, dirname } from "node:path";
|
|
13
12
|
import {
|
|
@@ -93,10 +92,10 @@ export function removeWorktree(repoDir, worktreePath, options = {}) {
|
|
|
93
92
|
let branch = null;
|
|
94
93
|
if (deleteBranch) {
|
|
95
94
|
try {
|
|
96
|
-
branch =
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
95
|
+
branch = gitExecArgs(
|
|
96
|
+
["rev-parse", "--abbrev-ref", "HEAD"],
|
|
97
|
+
worktreePath,
|
|
98
|
+
).trim();
|
|
100
99
|
} catch (_e) {
|
|
101
100
|
// Can't determine branch, skip branch deletion.
|
|
102
101
|
}
|
|
@@ -215,11 +214,7 @@ function _worktreeCleanupState(repoDir, wt) {
|
|
|
215
214
|
|
|
216
215
|
// Dirty state: separate tracked modifications from untracked files.
|
|
217
216
|
try {
|
|
218
|
-
const porcelain =
|
|
219
|
-
cwd: wt.path,
|
|
220
|
-
encoding: "utf-8",
|
|
221
|
-
maxBuffer: 64 * 1024 * 1024,
|
|
222
|
-
});
|
|
217
|
+
const porcelain = gitExecArgs(["status", "--porcelain"], wt.path);
|
|
223
218
|
const lines = porcelain.split("\n").filter((l) => l.trim().length > 0);
|
|
224
219
|
state.untracked = lines.some((l) => l.startsWith("??"));
|
|
225
220
|
state.uncommitted = lines.some((l) => !l.startsWith("??"));
|
|
@@ -393,8 +388,16 @@ export function mergeWorktree(repoDir, branchName, options = {}) {
|
|
|
393
388
|
};
|
|
394
389
|
} catch (err) {
|
|
395
390
|
const errMsg = err.message || String(err);
|
|
396
|
-
|
|
397
|
-
|
|
391
|
+
// Git can report merge conflicts on stdout while execSync's Error.message
|
|
392
|
+
// contains only the failed command (and localized installations need not
|
|
393
|
+
// include the English word "conflict"). The unmerged index is the
|
|
394
|
+
// authoritative signal.
|
|
395
|
+
const conflictPaths = _collectConflictFiles(repoDir);
|
|
396
|
+
if (
|
|
397
|
+
conflictPaths.length > 0 ||
|
|
398
|
+
errMsg.includes("CONFLICT") ||
|
|
399
|
+
errMsg.includes("conflict")
|
|
400
|
+
) {
|
|
398
401
|
if (conflictPaths.length === 0) {
|
|
399
402
|
conflictPaths.push(..._extractConflictPathsFromMessage(errMsg));
|
|
400
403
|
}
|
|
@@ -690,15 +693,9 @@ function _fileStatus(repoDir, base, branch, filePath) {
|
|
|
690
693
|
|
|
691
694
|
function _hasUncommittedChanges(worktreePath) {
|
|
692
695
|
try {
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
// A worktree with many dirty files produces >1 MB of porcelain output and
|
|
697
|
-
// would ENOBUFS on the default cap — the catch below then reports "clean"
|
|
698
|
-
// (fail-OPEN), which could let a caller discard a worktree full of
|
|
699
|
-
// uncommitted work. Give it the same 64 MB headroom as gitExec.
|
|
700
|
-
maxBuffer: 64 * 1024 * 1024,
|
|
701
|
-
});
|
|
696
|
+
// gitExecArgs carries the shared 64 MB maxBuffer, avoiding a fail-open
|
|
697
|
+
// "clean" result when a large worktree exceeds Node's default output cap.
|
|
698
|
+
const output = gitExecArgs(["status", "--porcelain"], worktreePath);
|
|
702
699
|
return output.trim().length > 0;
|
|
703
700
|
} catch (_e) {
|
|
704
701
|
return false;
|
package/src/lib/agent-core.js
CHANGED
|
@@ -13,6 +13,8 @@ export {
|
|
|
13
13
|
getAgentToolDescriptors,
|
|
14
14
|
getCachedPython,
|
|
15
15
|
getEnvironmentInfo,
|
|
16
|
+
_environmentProcessDeps,
|
|
17
|
+
_agentToolProcessDeps,
|
|
16
18
|
getBaseSystemPrompt,
|
|
17
19
|
buildSystemPrompt,
|
|
18
20
|
executeTool,
|
|
@@ -29,6 +31,9 @@ export {
|
|
|
29
31
|
killBackgroundShellTask,
|
|
30
32
|
killAllBackgroundShellTasks,
|
|
31
33
|
killAllBackgroundShellTasksSync,
|
|
34
|
+
_backgroundProcessDeps,
|
|
35
|
+
_runBackgroundTaskkill,
|
|
36
|
+
_gitProcessDeps,
|
|
32
37
|
reapIdleBackgroundShellTasks,
|
|
33
38
|
_ingestSearchOutput,
|
|
34
39
|
writeFileVerified,
|
package/src/lib/agent-ipc-bus.js
CHANGED
|
@@ -13,6 +13,11 @@ import { EventEmitter } from "node:events";
|
|
|
13
13
|
import crypto from "node:crypto";
|
|
14
14
|
import { EventRuntimeProducer } from "./event-runtime-producer.js";
|
|
15
15
|
import { EventRuntimeStore } from "./event-runtime-store.js";
|
|
16
|
+
import executionBroker from "./process-execution-broker/index.js";
|
|
17
|
+
|
|
18
|
+
export const _deps = {
|
|
19
|
+
spawn: executionBroker.spawn.bind(executionBroker),
|
|
20
|
+
};
|
|
16
21
|
|
|
17
22
|
/**
|
|
18
23
|
* @typedef {Object} InteractionRequest
|
|
@@ -170,12 +175,12 @@ class AgentIPCBus extends EventEmitter {
|
|
|
170
175
|
* @returns {Promise<{process: ChildProcess, agentId: string}>}
|
|
171
176
|
*/
|
|
172
177
|
async spawnAgentProcess(command, args = [], options = {}) {
|
|
173
|
-
const { spawn } = await import("node:child_process");
|
|
174
178
|
const agentId = options.agentId || crypto.randomUUID();
|
|
175
179
|
const heartbeatMs = options.heartbeatMs || 30000;
|
|
176
180
|
|
|
177
181
|
return new Promise((resolve, reject) => {
|
|
178
|
-
const
|
|
182
|
+
const spawnOptions = options.spawnOptions || {};
|
|
183
|
+
const child = _deps.spawn(command, args, {
|
|
179
184
|
stdio: ["pipe", "pipe", "pipe"],
|
|
180
185
|
env: {
|
|
181
186
|
...process.env,
|
|
@@ -184,7 +189,11 @@ class AgentIPCBus extends EventEmitter {
|
|
|
184
189
|
CHAINLESSCHAIN_IPC_PROTOCOL: "jsonrpc-stdio-v1",
|
|
185
190
|
...options.env,
|
|
186
191
|
},
|
|
187
|
-
...
|
|
192
|
+
...spawnOptions,
|
|
193
|
+
origin: spawnOptions.origin || "agent-ipc:subagent",
|
|
194
|
+
policy: spawnOptions.policy || "allow",
|
|
195
|
+
scope: spawnOptions.scope || "agent-ipc",
|
|
196
|
+
shell: spawnOptions.shell === true,
|
|
188
197
|
});
|
|
189
198
|
|
|
190
199
|
let buffer = "";
|
|
@@ -23,7 +23,12 @@ import {
|
|
|
23
23
|
} from "../../harness/worktree-isolator.js";
|
|
24
24
|
import { isGitRepo as _isGitRepo } from "../../lib/git-integration.js";
|
|
25
25
|
import { normalizeSparsePaths } from "../../lib/worktree-sparse.js";
|
|
26
|
-
import
|
|
26
|
+
import executionBroker from "../../lib/process-execution-broker/index.js";
|
|
27
|
+
|
|
28
|
+
export const _processDeps = {
|
|
29
|
+
spawn: executionBroker.spawn.bind(executionBroker),
|
|
30
|
+
execFileSync: executionBroker.execFileSync.bind(executionBroker),
|
|
31
|
+
};
|
|
27
32
|
|
|
28
33
|
/**
|
|
29
34
|
* Resolve the sparse-checkout / dependency-symlink options for a task, letting a
|
|
@@ -49,7 +54,14 @@ function resolveWorktreeOptions(task, defaults) {
|
|
|
49
54
|
|
|
50
55
|
function defaultRunShell(command, cwd) {
|
|
51
56
|
return new Promise((resolve, reject) => {
|
|
52
|
-
const child = spawn(command,
|
|
57
|
+
const child = _processDeps.spawn(command, [], {
|
|
58
|
+
cwd,
|
|
59
|
+
shell: true,
|
|
60
|
+
env: process.env,
|
|
61
|
+
origin: "team-worktree:task-command",
|
|
62
|
+
policy: "allow",
|
|
63
|
+
scope: "team-worktree",
|
|
64
|
+
});
|
|
53
65
|
let err = "";
|
|
54
66
|
child.stderr?.on("data", (d) => (err += d.toString("utf8")));
|
|
55
67
|
child.on("error", (e) => reject(new Error(e.message)));
|
|
@@ -61,12 +73,22 @@ function defaultRunShell(command, cwd) {
|
|
|
61
73
|
});
|
|
62
74
|
}
|
|
63
75
|
|
|
76
|
+
function runTeamGit(args, cwd) {
|
|
77
|
+
return _processDeps.execFileSync("git", args, {
|
|
78
|
+
cwd,
|
|
79
|
+
stdio: "ignore",
|
|
80
|
+
origin: "team-worktree:commit",
|
|
81
|
+
policy: "allow",
|
|
82
|
+
scope: "team-worktree",
|
|
83
|
+
shell: false,
|
|
84
|
+
});
|
|
85
|
+
}
|
|
86
|
+
|
|
64
87
|
/** Stage + commit everything in a worktree. Returns true if a commit was made. */
|
|
65
88
|
function defaultCommit(worktreePath, message) {
|
|
66
|
-
|
|
89
|
+
runTeamGit(["add", "-A"], worktreePath);
|
|
67
90
|
try {
|
|
68
|
-
|
|
69
|
-
"git",
|
|
91
|
+
runTeamGit(
|
|
70
92
|
[
|
|
71
93
|
"-c",
|
|
72
94
|
"user.email=team@chainlesschain.local",
|
|
@@ -76,7 +98,7 @@ function defaultCommit(worktreePath, message) {
|
|
|
76
98
|
"-m",
|
|
77
99
|
message,
|
|
78
100
|
],
|
|
79
|
-
|
|
101
|
+
worktreePath,
|
|
80
102
|
);
|
|
81
103
|
return true;
|
|
82
104
|
} catch {
|
|
@@ -11,24 +11,28 @@
|
|
|
11
11
|
* a merge hint. Removal failures fail-open to "kept".
|
|
12
12
|
*/
|
|
13
13
|
|
|
14
|
-
import { execFileSync, execSync } from "child_process";
|
|
15
14
|
import { existsSync, realpathSync } from "node:fs";
|
|
16
15
|
import { resolve } from "node:path";
|
|
17
16
|
import { createWorktree, removeWorktree } from "./worktree-isolator.js";
|
|
18
17
|
import { findProjectRoot } from "./project-instructions.js";
|
|
18
|
+
import executionBroker from "./process-execution-broker/index.js";
|
|
19
19
|
|
|
20
20
|
export const _deps = {
|
|
21
|
-
execFileSync,
|
|
22
|
-
execSync,
|
|
21
|
+
execFileSync: executionBroker.execFileSync.bind(executionBroker),
|
|
23
22
|
createWorktree,
|
|
24
23
|
removeWorktree,
|
|
25
24
|
};
|
|
26
25
|
|
|
27
|
-
function git(
|
|
28
|
-
|
|
26
|
+
function git(args, cwd, deps, options = {}) {
|
|
27
|
+
const run = deps.execFileSync || _deps.execFileSync;
|
|
28
|
+
return run("git", args, {
|
|
29
29
|
cwd,
|
|
30
30
|
encoding: "utf8",
|
|
31
|
-
stdio: ["ignore", "pipe", "ignore"],
|
|
31
|
+
stdio: options.stdio || ["ignore", "pipe", "ignore"],
|
|
32
|
+
origin: options.origin || "agent-worktree:query",
|
|
33
|
+
policy: "allow",
|
|
34
|
+
scope: "agent-worktree",
|
|
35
|
+
shell: false,
|
|
32
36
|
});
|
|
33
37
|
}
|
|
34
38
|
|
|
@@ -90,17 +94,17 @@ export function validateAgentWorktree(info, { deps = _deps } = {}) {
|
|
|
90
94
|
}
|
|
91
95
|
|
|
92
96
|
const repoTop = canonicalPath(
|
|
93
|
-
git("rev-parse --show-toplevel", info.repoRoot, deps).trim(),
|
|
97
|
+
git(["rev-parse", "--show-toplevel"], info.repoRoot, deps).trim(),
|
|
94
98
|
);
|
|
95
99
|
const worktreeTop = canonicalPath(
|
|
96
|
-
git("rev-parse --show-toplevel", worktreePath, deps).trim(),
|
|
100
|
+
git(["rev-parse", "--show-toplevel"], worktreePath, deps).trim(),
|
|
97
101
|
);
|
|
98
102
|
if (repoTop !== repo || worktreeTop !== worktree) {
|
|
99
103
|
return { valid: false, reason: "worktree path identity mismatch" };
|
|
100
104
|
}
|
|
101
105
|
|
|
102
106
|
const registered = parseRegisteredWorktrees(
|
|
103
|
-
git("worktree list --porcelain", info.repoRoot, deps),
|
|
107
|
+
git(["worktree", "list", "--porcelain"], info.repoRoot, deps),
|
|
104
108
|
).find((entry) => {
|
|
105
109
|
try {
|
|
106
110
|
return canonicalPath(entry.path) === worktree;
|
|
@@ -138,16 +142,13 @@ function removeVerifiedAgentWorktree(info, deps) {
|
|
|
138
142
|
return null;
|
|
139
143
|
}
|
|
140
144
|
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
cwd: info.repoRoot,
|
|
144
|
-
encoding: "utf8",
|
|
145
|
+
git(["worktree", "remove", info.path], info.repoRoot, deps, {
|
|
146
|
+
origin: "agent-worktree:cleanup",
|
|
145
147
|
stdio: ["ignore", "pipe", "pipe"],
|
|
146
148
|
});
|
|
147
149
|
try {
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
encoding: "utf8",
|
|
150
|
+
git(["branch", "-D", "--", info.branch], info.repoRoot, deps, {
|
|
151
|
+
origin: "agent-worktree:cleanup",
|
|
151
152
|
stdio: ["ignore", "pipe", "pipe"],
|
|
152
153
|
});
|
|
153
154
|
return null;
|
|
@@ -174,7 +175,7 @@ export function setupAgentWorktree({
|
|
|
174
175
|
const suffix = Math.random().toString(36).slice(2, 6);
|
|
175
176
|
const branch = `cc-agent-${stamp}-${suffix}`;
|
|
176
177
|
const info = (deps.createWorktree || createWorktree)(repoRoot, branch);
|
|
177
|
-
const baseSha = git("rev-parse HEAD", info.path, deps).trim();
|
|
178
|
+
const baseSha = git(["rev-parse", "HEAD"], info.path, deps).trim();
|
|
178
179
|
return { ...info, repoRoot, baseSha };
|
|
179
180
|
}
|
|
180
181
|
|
|
@@ -205,8 +206,8 @@ export function finishAgentWorktree(info, { deps = _deps } = {}) {
|
|
|
205
206
|
let headSha = null;
|
|
206
207
|
let readable = false;
|
|
207
208
|
try {
|
|
208
|
-
dirty = git("status --porcelain", info.path, deps).trim().length > 0;
|
|
209
|
-
headSha = git("rev-parse HEAD", info.path, deps).trim();
|
|
209
|
+
dirty = git(["status", "--porcelain"], info.path, deps).trim().length > 0;
|
|
210
|
+
headSha = git(["rev-parse", "HEAD"], info.path, deps).trim();
|
|
210
211
|
readable = true;
|
|
211
212
|
} catch {
|
|
212
213
|
/* unreadable → keep, never destroy work we cannot verify */
|
|
@@ -28,11 +28,10 @@
|
|
|
28
28
|
* - tree-reaping of every child on stopAll() / process 'exit', so a session
|
|
29
29
|
* never leaves an orphan hook process.
|
|
30
30
|
*
|
|
31
|
-
*
|
|
31
|
+
* Process runners and `_deps.now` are injected so the whole supervisor is unit-
|
|
32
32
|
* testable with fake child processes (no real shell).
|
|
33
33
|
*/
|
|
34
34
|
|
|
35
|
-
const cpDefault = require("node:child_process");
|
|
36
35
|
const { tryParseDecision } = require("./hook-runner.cjs");
|
|
37
36
|
|
|
38
37
|
const DEFAULT_TIMEOUT_MS = 60000;
|
|
@@ -52,8 +51,10 @@ class AsyncHookSupervisor {
|
|
|
52
51
|
? Number(opts.maxConcurrent)
|
|
53
52
|
: DEFAULT_MAX_CONCURRENT;
|
|
54
53
|
this._deps = {
|
|
55
|
-
|
|
56
|
-
|
|
54
|
+
run: opts.run || opts.spawn || null,
|
|
55
|
+
runSync: opts.runSync || opts.spawnSync || null,
|
|
56
|
+
killProcess: opts.killProcess || process.kill.bind(process),
|
|
57
|
+
platform: opts.platform || process.platform,
|
|
57
58
|
now: opts.now || (() => Date.now()),
|
|
58
59
|
};
|
|
59
60
|
this._running = new Map(); // key -> { child, timer }
|
|
@@ -125,8 +126,12 @@ class AsyncHookSupervisor {
|
|
|
125
126
|
return;
|
|
126
127
|
}
|
|
127
128
|
try {
|
|
128
|
-
if (
|
|
129
|
-
|
|
129
|
+
if (this._deps.platform === "win32") {
|
|
130
|
+
// Snapshot before taskkill: a policy-restricted taskkill may terminate
|
|
131
|
+
// the shell and then fail on its descendants, orphaning them so a
|
|
132
|
+
// parent-PID query performed afterward can no longer find the tree.
|
|
133
|
+
const descendants = this._windowsDescendantPids(pid);
|
|
134
|
+
const r = this._deps.runSync(
|
|
130
135
|
"taskkill",
|
|
131
136
|
["/PID", String(pid), "/T", "/F"],
|
|
132
137
|
{ windowsHide: true },
|
|
@@ -135,20 +140,27 @@ class AsyncHookSupervisor {
|
|
|
135
140
|
// tree snapshot and termination. Repeat once so that race cannot
|
|
136
141
|
// leave a short-lived hook grandchild orphaned.
|
|
137
142
|
if (!r?.error && r?.status === 0) {
|
|
138
|
-
this._deps.
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
{ windowsHide: true },
|
|
142
|
-
);
|
|
143
|
+
this._deps.runSync("taskkill", ["/PID", String(pid), "/T", "/F"], {
|
|
144
|
+
windowsHide: true,
|
|
145
|
+
});
|
|
143
146
|
}
|
|
144
|
-
//
|
|
147
|
+
// Restricted runners can launch taskkill but still receive a non-zero
|
|
148
|
+
// access/policy result. Enumerate the tree while it is still alive,
|
|
149
|
+
// then terminate descendants leaf-first before killing the shell.
|
|
145
150
|
if (r && (r.error || (r.status != null && r.status !== 0))) {
|
|
151
|
+
for (const descendant of descendants.reverse()) {
|
|
152
|
+
try {
|
|
153
|
+
this._deps.killProcess(descendant, "SIGKILL");
|
|
154
|
+
} catch {
|
|
155
|
+
/* already gone */
|
|
156
|
+
}
|
|
157
|
+
}
|
|
146
158
|
child.kill(signal);
|
|
147
159
|
}
|
|
148
160
|
} else {
|
|
149
161
|
// Negative pid → the whole process group (requires the detached spawn).
|
|
150
162
|
try {
|
|
151
|
-
|
|
163
|
+
this._deps.killProcess(-pid, signal);
|
|
152
164
|
} catch {
|
|
153
165
|
child.kill(signal);
|
|
154
166
|
}
|
|
@@ -162,6 +174,102 @@ class AsyncHookSupervisor {
|
|
|
162
174
|
}
|
|
163
175
|
}
|
|
164
176
|
|
|
177
|
+
/**
|
|
178
|
+
* Snapshot a Windows process tree for the taskkill-denied fallback. Query the
|
|
179
|
+
* process table once instead of issuing a quoted WMIC `where` expression for
|
|
180
|
+
* every level: Node/cmd quoting can silently turn that expression into an
|
|
181
|
+
* invalid WMIC alias query. PowerShell/CIM covers systems where WMIC has been
|
|
182
|
+
* removed. Traversal is bounded + cycle-safe so corrupt data cannot stall
|
|
183
|
+
* session teardown.
|
|
184
|
+
*/
|
|
185
|
+
_windowsDescendantPids(rootPid) {
|
|
186
|
+
if (this._deps.platform !== "win32") return [];
|
|
187
|
+
const root = Number(rootPid);
|
|
188
|
+
if (!Number.isInteger(root) || root <= 0) return [];
|
|
189
|
+
let output = "";
|
|
190
|
+
let shouldTryCim = false;
|
|
191
|
+
try {
|
|
192
|
+
const result = this._deps.runSync(
|
|
193
|
+
"wmic",
|
|
194
|
+
[
|
|
195
|
+
"path",
|
|
196
|
+
"Win32_Process",
|
|
197
|
+
"get",
|
|
198
|
+
"ParentProcessId,ProcessId",
|
|
199
|
+
"/format:csv",
|
|
200
|
+
],
|
|
201
|
+
{
|
|
202
|
+
encoding: "utf8",
|
|
203
|
+
windowsHide: true,
|
|
204
|
+
timeout: 3000,
|
|
205
|
+
},
|
|
206
|
+
);
|
|
207
|
+
if (!result?.error && result?.status === 0) {
|
|
208
|
+
output = String(result.stdout || "");
|
|
209
|
+
} else if (result?.error?.code === "ENOENT") {
|
|
210
|
+
shouldTryCim = true;
|
|
211
|
+
}
|
|
212
|
+
} catch {
|
|
213
|
+
shouldTryCim = true;
|
|
214
|
+
}
|
|
215
|
+
if (!output && shouldTryCim) {
|
|
216
|
+
try {
|
|
217
|
+
const result = this._deps.runSync(
|
|
218
|
+
"powershell.exe",
|
|
219
|
+
[
|
|
220
|
+
"-NoProfile",
|
|
221
|
+
"-NonInteractive",
|
|
222
|
+
"-Command",
|
|
223
|
+
"Get-CimInstance Win32_Process | ForEach-Object { '{0},{1}' -f $_.ParentProcessId,$_.ProcessId }",
|
|
224
|
+
],
|
|
225
|
+
{
|
|
226
|
+
encoding: "utf8",
|
|
227
|
+
windowsHide: true,
|
|
228
|
+
timeout: 3000,
|
|
229
|
+
},
|
|
230
|
+
);
|
|
231
|
+
if (!result?.error && result?.status === 0) {
|
|
232
|
+
output = String(result.stdout || "");
|
|
233
|
+
}
|
|
234
|
+
} catch {
|
|
235
|
+
return [];
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
const childrenByParent = new Map();
|
|
240
|
+
for (const line of output.split(/\r?\n/)) {
|
|
241
|
+
const fields = line.trim().split(",");
|
|
242
|
+
if (fields.length < 2) continue;
|
|
243
|
+
const parentPid = Number(fields.at(-2));
|
|
244
|
+
const childPid = Number(fields.at(-1));
|
|
245
|
+
if (
|
|
246
|
+
!Number.isInteger(parentPid) ||
|
|
247
|
+
parentPid < 0 ||
|
|
248
|
+
!Number.isInteger(childPid) ||
|
|
249
|
+
childPid <= 0
|
|
250
|
+
) {
|
|
251
|
+
continue;
|
|
252
|
+
}
|
|
253
|
+
const children = childrenByParent.get(parentPid) || [];
|
|
254
|
+
children.push(childPid);
|
|
255
|
+
childrenByParent.set(parentPid, children);
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
const seen = new Set([root]);
|
|
259
|
+
const pending = [root];
|
|
260
|
+
const descendants = [];
|
|
261
|
+
while (pending.length > 0 && seen.size <= 256) {
|
|
262
|
+
const parentPid = pending.shift();
|
|
263
|
+
for (const childPid of childrenByParent.get(parentPid) || []) {
|
|
264
|
+
if (seen.has(childPid)) continue;
|
|
265
|
+
seen.add(childPid);
|
|
266
|
+
descendants.push(childPid);
|
|
267
|
+
pending.push(childPid);
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
return descendants;
|
|
271
|
+
}
|
|
272
|
+
|
|
165
273
|
/** Install the process-exit reaper exactly once, only when a child exists. */
|
|
166
274
|
_installExitHook() {
|
|
167
275
|
if (this._exitHookInstalled) return;
|
|
@@ -257,22 +365,24 @@ class AsyncHookSupervisor {
|
|
|
257
365
|
// POSIX: own process group so a timeout / stopAll can signal the whole
|
|
258
366
|
// tree (shell + the real hook command), not just the shell wrapper.
|
|
259
367
|
// No-op on Windows, where the tree is reaped via `taskkill /T` instead.
|
|
260
|
-
detached:
|
|
368
|
+
detached: this._deps.platform !== "win32",
|
|
261
369
|
env: {
|
|
262
370
|
...process.env,
|
|
263
371
|
CLAUDE_HOOK_EVENT: hook.event || payload.hook_event_name || "",
|
|
264
372
|
},
|
|
265
373
|
};
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
:
|
|
374
|
+
if (typeof this._deps.run !== "function") {
|
|
375
|
+
throw new Error("async hook process runner unavailable");
|
|
376
|
+
}
|
|
377
|
+
child = this._deps.run(hook.command, {
|
|
378
|
+
...spawnOptions,
|
|
379
|
+
origin: hook.origin || "async-hook:command",
|
|
380
|
+
policy: "allow",
|
|
381
|
+
scope: "async-hook",
|
|
382
|
+
pluginId: hook.pluginId || null,
|
|
383
|
+
pluginVersion: hook.pluginVersion || null,
|
|
384
|
+
pluginSource: hook.pluginSource || null,
|
|
385
|
+
});
|
|
276
386
|
} catch (err) {
|
|
277
387
|
this._record(hook, {
|
|
278
388
|
ok: false,
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import supervisor from "./async-hook-supervisor.cjs";
|
|
2
|
+
import executionBroker from "./process-execution-broker/index.js";
|
|
3
|
+
|
|
4
|
+
const brokerRunner = executionBroker.spawn.bind(executionBroker);
|
|
5
|
+
const brokerSyncRunner = executionBroker.spawnSync.bind(executionBroker);
|
|
6
|
+
|
|
7
|
+
export const _processDeps = {
|
|
8
|
+
run: brokerRunner,
|
|
9
|
+
runSync: brokerSyncRunner,
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
function runAsyncHook(command, options = {}) {
|
|
13
|
+
return _processDeps.run(command, [], {
|
|
14
|
+
...options,
|
|
15
|
+
origin: options.origin || "async-hook:command",
|
|
16
|
+
policy: "allow",
|
|
17
|
+
scope: "async-hook",
|
|
18
|
+
});
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
function runAsyncHookSupervisorCommand(command, args, options = {}) {
|
|
22
|
+
return _processDeps.runSync(command, args, {
|
|
23
|
+
...options,
|
|
24
|
+
origin: options.origin || "async-hook:supervisor",
|
|
25
|
+
policy: "allow",
|
|
26
|
+
scope: "async-hook",
|
|
27
|
+
});
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export class AsyncHookSupervisor extends supervisor.AsyncHookSupervisor {
|
|
31
|
+
constructor(options = {}) {
|
|
32
|
+
super({
|
|
33
|
+
...options,
|
|
34
|
+
run: options.run || options.spawn || runAsyncHook,
|
|
35
|
+
runSync:
|
|
36
|
+
options.runSync || options.spawnSync || runAsyncHookSupervisorCommand,
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export const { DEFAULT_TIMEOUT_MS, DEFAULT_MAX_CONCURRENT } = supervisor;
|
|
42
|
+
|
|
43
|
+
export default {
|
|
44
|
+
AsyncHookSupervisor,
|
|
45
|
+
DEFAULT_TIMEOUT_MS,
|
|
46
|
+
DEFAULT_MAX_CONCURRENT,
|
|
47
|
+
};
|