chainlesschain 0.162.175 → 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 +10 -10
- package/src/assets/web-panel/assets/{AIOps-DherW0Rh.js → AIOps-DT-VPWuN.js} +1 -1
- package/src/assets/web-panel/assets/{ActionButton-CDGfqt2Y.js → ActionButton-DyOreOVN.js} +1 -1
- package/src/assets/web-panel/assets/{Analytics-BYhkzdgD.js → Analytics-DslNsKux.js} +3 -3
- package/src/assets/web-panel/assets/{AppLayout-cEKk1cpb.js → AppLayout-BNJJAYME.js} +5 -5
- package/src/assets/web-panel/assets/{Artifacts-_aXR3WWc.js → Artifacts-DoaAtEcY.js} +1 -1
- package/src/assets/web-panel/assets/{Audit-CYAf-gY7.js → Audit-DPG3WdJ9.js} +1 -1
- package/src/assets/web-panel/assets/{BackgroundAgents-D4Q20phR.js → BackgroundAgents-C_JO6R5G.js} +1 -1
- package/src/assets/web-panel/assets/{Backup-Cq0Tyk58.js → Backup-Cv3BooyX.js} +1 -1
- package/src/assets/web-panel/assets/{BaseInput-CPj_CdAv.js → BaseInput-D2uQXWu-.js} +1 -1
- package/src/assets/web-panel/assets/{Chat-BQ-Qq0es.js → Chat-B4Vczyf3.js} +4 -4
- package/src/assets/web-panel/assets/ChatBubbleRenderer-DE6reP99.js +1 -0
- package/src/assets/web-panel/assets/{Checkbox-rke6l3yg.js → Checkbox-qTgk4VeP.js} +1 -1
- package/src/assets/web-panel/assets/{Codegen-DiacrIPq.js → Codegen-DlLWcIzj.js} +1 -1
- package/src/assets/web-panel/assets/{Col-Cx3BY-Up.js → Col-DNO73qp7.js} +1 -1
- package/src/assets/web-panel/assets/{Community-DfmWiQy8.js → Community-vRDNpc4j.js} +1 -1
- package/src/assets/web-panel/assets/{Compact-DdQnpxy5.js → Compact-CEWtIXni.js} +1 -1
- package/src/assets/web-panel/assets/{Compliance-lzliOH_r.js → Compliance-KgYC9Fxi.js} +1 -1
- package/src/assets/web-panel/assets/{Cowork-CYx4Un5k.js → Cowork-zj5wvkpT.js} +2 -2
- package/src/assets/web-panel/assets/{Cron-BGDcPFlo.js → Cron-BLDoCozU.js} +2 -2
- package/src/assets/web-panel/assets/{Crosschain-0_X5g2ri.js → Crosschain-BYnff4VY.js} +1 -1
- package/src/assets/web-panel/assets/{DID-C0PEf3Eu.js → DID-C75VFsVm.js} +2 -2
- package/src/assets/web-panel/assets/{Dashboard-DULQjQ58.js → Dashboard-C4Gutni2.js} +2 -2
- package/src/assets/web-panel/assets/{Dropdown-DGRHRbW6.js → Dropdown-B5dbO4Kx.js} +1 -1
- package/src/assets/web-panel/assets/{EmailListRenderer-ChX8XPwB.js → EmailListRenderer-BDipZtHJ.js} +1 -1
- package/src/assets/web-panel/assets/{FamilyGuardDashboard-Dyq6IBNu.js → FamilyGuardDashboard-DmIzt8jj.js} +1 -1
- package/src/assets/web-panel/assets/{Federation-DH1NfAL2.js → Federation-D9QOS-uW.js} +1 -1
- package/src/assets/web-panel/assets/{FormItemContext-BN1I_uNi.js → FormItemContext-B47HRwAm.js} +1 -1
- package/src/assets/web-panel/assets/{GenericCardRenderer-Dq_2UYpe.js → GenericCardRenderer-3R4mXmgV.js} +1 -1
- package/src/assets/web-panel/assets/{Git-BAy1yvI2.js → Git-DrNXZf1C.js} +2 -2
- package/src/assets/web-panel/assets/{Governance-Db6Q2iSy.js → Governance-BGd7JR98.js} +1 -1
- package/src/assets/web-panel/assets/{Inference-0ub6yHcr.js → Inference-D-2he1ed.js} +1 -1
- package/src/assets/web-panel/assets/{KnowledgeGraph-DtLetM36.js → KnowledgeGraph-C0sHIbK5.js} +1 -1
- package/src/assets/web-panel/assets/{Logs-Bcoy7CUd.js → Logs-DiBQO-jL.js} +2 -2
- package/src/assets/web-panel/assets/{MarkdownRenderer-BfEMFAXf.js → MarkdownRenderer-DL_5eV2D.js} +1 -1
- package/src/assets/web-panel/assets/{Marketplace-tt2rzwRR.js → Marketplace-VR59OdrC.js} +1 -1
- package/src/assets/web-panel/assets/{McpTools-BhOgX-MU.js → McpTools-DrPzwSd3.js} +3 -3
- package/src/assets/web-panel/assets/{Memory-BJUxeWBL.js → Memory-CBgKALwQ.js} +2 -2
- package/src/assets/web-panel/assets/{MobileBridge-BwUY2G9w.js → MobileBridge-pQ9Tn-Mn.js} +1 -1
- package/src/assets/web-panel/assets/{MobileProjects-BLRfHhqi.js → MobileProjects-mASB6jyj.js} +1 -1
- package/src/assets/web-panel/assets/{Mtc-B2WMboOm.js → Mtc-DTMgE7HC.js} +2 -2
- package/src/assets/web-panel/assets/{MtcAudit-8pNEXO63.js → MtcAudit-CwTnCtiL.js} +2 -2
- package/src/assets/web-panel/assets/{Multisig-C1wm5a_1.js → Multisig-ngm6WU8f.js} +3 -3
- package/src/assets/web-panel/assets/{NLProgramming-C1QpKF7g.js → NLProgramming-4YO4Qww9.js} +1 -1
- package/src/assets/web-panel/assets/{Notes-CiQUcxFu.js → Notes-DP1k45mF.js} +3 -3
- package/src/assets/web-panel/assets/{NotificationSettings-BblDPJ_x.js → NotificationSettings-r2H8yPKb.js} +1 -1
- package/src/assets/web-panel/assets/{OrderTableRenderer-DRjYcPtb.js → OrderTableRenderer-CoM_Zw0J.js} +1 -1
- package/src/assets/web-panel/assets/{Organization-Dp3VKB0X.js → Organization-DrO7jRWS.js} +4 -4
- package/src/assets/web-panel/assets/{Overflow-Czx161m9.js → Overflow-Cnwu4i4t.js} +1 -1
- package/src/assets/web-panel/assets/{P2P-ClFi6wOs.js → P2P-zi-cDwA_.js} +2 -2
- package/src/assets/web-panel/assets/{PdhVaultBrowser-CRweW0lz.js → PdhVaultBrowser-DBEryDv7.js} +3 -3
- package/src/assets/web-panel/assets/{Permissions-BzcRoqTn.js → Permissions-lqXK2C3x.js} +4 -4
- package/src/assets/web-panel/assets/{PersonalDataHub-ChGTpNMd.js → PersonalDataHub-b63T3HJM.js} +2 -2
- package/src/assets/web-panel/assets/{Pipeline-BMOOpKWA.js → Pipeline-Kep5KAGW.js} +1 -1
- package/src/assets/web-panel/assets/{Privacy-D0AZHObH.js → Privacy-CHdvOAh1.js} +1 -1
- package/src/assets/web-panel/assets/{ProjectInit-yYFlZ6Ja.js → ProjectInit-BhKfdwe9.js} +2 -2
- package/src/assets/web-panel/assets/{ProjectSettings-DJNVuMQs.js → ProjectSettings-Yavk9K8A.js} +2 -2
- package/src/assets/web-panel/assets/{Projects-Br0rBWH1.js → Projects-CN8E_5Mq.js} +1 -1
- package/src/assets/web-panel/assets/Providers-Bfym6SQn.js +1 -0
- package/src/assets/web-panel/assets/{QrScannerModal-81NZOitV.js → QrScannerModal-CLSQcrlR.js} +1 -1
- package/src/assets/web-panel/assets/{QuickAsk-Dic2lCbJ.js → QuickAsk-VPa2WKQN.js} +1 -1
- package/src/assets/web-panel/assets/{Recommend-Dh9iwViE.js → Recommend-CGXR8jVU.js} +1 -1
- package/src/assets/web-panel/assets/{RemoteSession-Bq9VYEFy.js → RemoteSession-C_20YfMB.js} +2 -2
- package/src/assets/web-panel/assets/{Reputation-CxFZ-NbJ.js → Reputation-awZh68UV.js} +1 -1
- package/src/assets/web-panel/assets/{Row-BVSLcaX2.js → Row-Ctb4oiYN.js} +1 -1
- package/src/assets/web-panel/assets/{RssFeed-BbC-5DOH.js → RssFeed-D3sdjvUV.js} +3 -3
- package/src/assets/web-panel/assets/{Search-BfjZ1GCr.js → Search-C-zfA4kT.js} +1 -1
- package/src/assets/web-panel/assets/{Security-CVjhOV5r.js → Security-D9XlmHA3.js} +4 -4
- package/src/assets/web-panel/assets/{Services-D45u-Aqe.js → Services-C3UKnr41.js} +2 -2
- package/src/assets/web-panel/assets/{Skeleton-WkGoK4he.js → Skeleton-BQxagZfF.js} +1 -1
- package/src/assets/web-panel/assets/Skills-q6eb6KPZ.js +1 -0
- package/src/assets/web-panel/assets/{Sla-Bf4QTF3E.js → Sla-DveENvek.js} +1 -1
- package/src/assets/web-panel/assets/{SpeechSettings-DVpZdbMN.js → SpeechSettings-BFjXLNgF.js} +1 -1
- package/src/assets/web-panel/assets/{SyncSettings-DEEojoMS.js → SyncSettings-BpHgbaP9.js} +2 -2
- package/src/assets/web-panel/assets/Tasks-DvepRb8-.js +1 -0
- package/src/assets/web-panel/assets/{Templates-Bzl-9wY5.js → Templates-DEiaoyve.js} +1 -1
- package/src/assets/web-panel/assets/{Tenant-0khxUOB8.js → Tenant-ITg774nY.js} +1 -1
- package/src/assets/web-panel/assets/{Terminal-DWGYhAYU.js → Terminal-D3ZBcvnb.js} +2 -2
- package/src/assets/web-panel/assets/{TimelineRenderer-B0Y4LONc.js → TimelineRenderer-BsLU6JjU.js} +1 -1
- package/src/assets/web-panel/assets/{Tokens-DEE07SFh.js → Tokens-Cb5Pquc9.js} +1 -1
- package/src/assets/web-panel/assets/{Trigger-BM7xvA5z.js → Trigger-Dwfy-m9N.js} +1 -1
- package/src/assets/web-panel/assets/{Trust-Dv3ijvvZ.js → Trust-CnlSRGG0.js} +1 -1
- package/src/assets/web-panel/assets/{UkeySign-B1-enmMt.js → UkeySign-Y4TlvKVo.js} +1 -1
- package/src/assets/web-panel/assets/{VideoEditing-DYiIPByd.js → VideoEditing-BpKLtTTO.js} +1 -1
- package/src/assets/web-panel/assets/{Wallet-D-y374BQ.js → Wallet-LLBzIq8x.js} +4 -4
- package/src/assets/web-panel/assets/{WebAuthn-HBDjcUhi.js → WebAuthn-BeSRiusP.js} +4 -4
- package/src/assets/web-panel/assets/{WorkflowEditor-sKn4qTID.js → WorkflowEditor-BK_l1tXO.js} +1 -1
- package/src/assets/web-panel/assets/{chat-TVYhaBXj.js → chat-DYd-X7Wg.js} +1 -1
- package/src/assets/web-panel/assets/{colors-DMADxY9v.js → colors-C9uip71j.js} +1 -1
- package/src/assets/web-panel/assets/{compact-item-OftcHpO_.js → compact-item-JBrfjUP6.js} +1 -1
- package/src/assets/web-panel/assets/{createContext-DW1zVUSN.js → createContext-D03fbuiq.js} +1 -1
- package/src/assets/web-panel/assets/devWarning-B8LQ8leS.js +1 -0
- package/src/assets/web-panel/assets/{hasIn-CaUathNp.js → hasIn-DLQOR6l-.js} +1 -1
- package/src/assets/web-panel/assets/{index-Cmdv6Dev.js → index-5YN7F5bO.js} +1 -1
- package/src/assets/web-panel/assets/{index-iDmgT2Dg.js → index-6GuLRpBQ.js} +1 -1
- package/src/assets/web-panel/assets/{index-77kgkUgY.js → index-B0X76LjX.js} +1 -1
- package/src/assets/web-panel/assets/{index-CM8W07sc.js → index-B46YcgsV.js} +1 -1
- package/src/assets/web-panel/assets/{index-C9JoaYSs.js → index-BNCdRmWf.js} +1 -1
- package/src/assets/web-panel/assets/{index-CpDtN0dl.js → index-BYv-LWdy.js} +1 -1
- package/src/assets/web-panel/assets/{index-CZXYI5LU.js → index-Ba-Ps_AJ.js} +1 -1
- package/src/assets/web-panel/assets/{index-DWVZ3T0t.js → index-BcBZwlnG.js} +1 -1
- package/src/assets/web-panel/assets/{index--5UvwqH_.js → index-BdH9l8wl.js} +1 -1
- package/src/assets/web-panel/assets/{index-BBNg08cD.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-s-EnkUCn.js → index-BnQgRjsm.js} +1 -1
- package/src/assets/web-panel/assets/{index-Lr0MO-GU.js → index-BpYKLrUT.js} +1 -1
- package/src/assets/web-panel/assets/{index-B-19mFhn.js → index-BuNFCZCx.js} +1 -1
- package/src/assets/web-panel/assets/{index-CeCIYYG7.js → index-ByHO3SqZ.js} +1 -1
- package/src/assets/web-panel/assets/{index-hXq7QRA_.js → index-C2zmOsyD.js} +1 -1
- package/src/assets/web-panel/assets/{index-8YelceUU.js → index-CChQBM1d.js} +1 -1
- package/src/assets/web-panel/assets/{index-B52Ztt8Q.js → index-CChaISMb.js} +1 -1
- package/src/assets/web-panel/assets/{index-DKjqFpYa.js → index-CILVyK3b.js} +1 -1
- package/src/assets/web-panel/assets/{index-DAfT3EMd.js → index-CL0ixCxk.js} +1 -1
- package/src/assets/web-panel/assets/{index-sm1sGIxW.js → index-CS63r8h3.js} +1 -1
- package/src/assets/web-panel/assets/{index-DHi634g9.js → index-CVZWiUE_.js} +1 -1
- package/src/assets/web-panel/assets/{index-BEzLLVYd.js → index-Ce0Bf1YF.js} +1 -1
- package/src/assets/web-panel/assets/index-CeVrHLzZ.js +1 -0
- package/src/assets/web-panel/assets/{index-pSVCBsG4.js → index-Ck17i0OD.js} +1 -1
- package/src/assets/web-panel/assets/{index-GUKz4Pv9.js → index-Cpn19c9k.js} +1 -1
- package/src/assets/web-panel/assets/{index-CKNdnJRo.js → index-CqWk1FLG.js} +1 -1
- package/src/assets/web-panel/assets/{index-Bg-7FpaL.js → index-CwoDpKzW.js} +1 -1
- package/src/assets/web-panel/assets/{index-DYwwUKDg.js → index-D-1SqEpQ.js} +1 -1
- package/src/assets/web-panel/assets/{index-DZdT8nio.js → index-D4UY5Z60.js} +1 -1
- package/src/assets/web-panel/assets/{index-BvRw4Fna.js → index-D8KvuDz0.js} +1 -1
- package/src/assets/web-panel/assets/{index-kdMl_0zL.js → index-DNMAAUVk.js} +1 -1
- package/src/assets/web-panel/assets/{index-CK-p7Hji.js → index-DYLZY4uh.js} +1 -1
- package/src/assets/web-panel/assets/{index-DXcqvWBp.js → index-DaokIHt3.js} +1 -1
- package/src/assets/web-panel/assets/{index-BFVbDpqg.js → index-Q7mmL6w4.js} +1 -1
- package/src/assets/web-panel/assets/{index-BJ1PjRY3.js → index-SO6lgMg0.js} +1 -1
- package/src/assets/web-panel/assets/{index-BSp1Ofvx.js → index-iahNhZwm.js} +1 -1
- package/src/assets/web-panel/assets/{index-BupSWDcl.js → index-l08afIbV.js} +1 -1
- package/src/assets/web-panel/assets/{index-D63QVAiJ.js → index-rIxxfdTN.js} +1 -1
- package/src/assets/web-panel/assets/{initDefaultProps-DdcKTlQR.js → initDefaultProps-CG9z1c6w.js} +1 -1
- package/src/assets/web-panel/assets/{motion-CL-hTBQj.js → motion-B1Ky7Zff.js} +1 -1
- package/src/assets/web-panel/assets/{move-qYSAkxNG.js → move-DME8X2P1.js} +1 -1
- package/src/assets/web-panel/assets/{mtc-parser-ZwRSZtRP.js → mtc-parser-cuJYSWB7.js} +1 -1
- package/src/assets/web-panel/assets/{omit-Bld-8ftI.js → omit-UI_EE0uZ.js} +1 -1
- package/src/assets/web-panel/assets/{pickAttrs-DGHs-a9Z.js → pickAttrs-BIoE_Z_V.js} +1 -1
- package/src/assets/web-panel/assets/{placementArrow-DRQPMyI5.js → placementArrow-B48-9HKN.js} +1 -1
- package/src/assets/web-panel/assets/{responsiveObserve-C5MJG_Yo.js → responsiveObserve-D7x_GDgD.js} +1 -1
- package/src/assets/web-panel/assets/{slide-VZ-fpZCD.js → slide-DjzC5_7m.js} +1 -1
- package/src/assets/web-panel/assets/{statusUtils-9ocfCyHk.js → statusUtils-C6BIymhh.js} +1 -1
- package/src/assets/web-panel/assets/{styleChecker-B6QAwAN5.js → styleChecker-B4dGrdD9.js} +1 -1
- package/src/assets/web-panel/assets/{useFlexGapSupport-Bq1twor3.js → useFlexGapSupport-Bken4aNE.js} +1 -1
- package/src/assets/web-panel/assets/{useFs-DKZq96tz.js → useFs-BMlqECqv.js} +1 -1
- package/src/assets/web-panel/assets/{usePersonalDataHub-D0RmRVXf.js → usePersonalDataHub-DLdrfmMa.js} +1 -1
- package/src/assets/web-panel/assets/{vnode-D6kEj_tt.js → vnode-CKJQmxa0.js} +1 -1
- package/src/assets/web-panel/assets/{zoom-2yfasoQf.js → zoom-CaZunuau.js} +1 -1
- package/src/assets/web-panel/index.html +1 -1
- package/src/auth/npm-auth.js +30 -5
- package/src/commands/agenda.js +83 -5
- package/src/commands/agent.js +42 -4
- 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/config.js +70 -5
- package/src/commands/context.js +36 -2
- package/src/commands/eval.js +21 -6
- package/src/commands/hook.js +2 -2
- package/src/commands/hub.js +19 -1
- package/src/commands/loop.js +9 -2
- package/src/commands/memory.js +21 -6
- package/src/commands/permissions.js +57 -1
- package/src/commands/plugin.js +2 -0
- package/src/commands/review.js +23 -11
- package/src/commands/routine.js +31 -8
- package/src/commands/session.js +11 -3
- package/src/commands/skill.js +2 -0
- package/src/commands/statusline.js +3 -3
- package/src/commands/team.js +20 -7
- package/src/commands/update.js +65 -10
- package/src/data/changelog.json +293 -51
- package/src/gateways/terminal/PtyManager.js +8 -1
- package/src/gateways/ws/message-dispatcher.js +3 -0
- package/src/gateways/ws/session-protocol.js +26 -0
- package/src/gateways/ws/ws-agent-handler.js +107 -69
- package/src/gateways/ws/ws-server.js +74 -2
- package/src/harness/background-task-manager.js +18 -6
- package/src/harness/background-task-worker.js +11 -18
- package/src/harness/jsonl-session-store.js +13 -6
- package/src/harness/mcp-client.js +195 -2
- package/src/harness/worktree-isolator.js +18 -21
- package/src/lib/__tests__/personal-data-hub-aichat-wizard.test.js +15 -0
- package/src/lib/agent-coordinator.js +4 -0
- package/src/lib/agent-core.js +5 -0
- package/src/lib/agent-ipc-bus.js +44 -5
- package/src/lib/agent-sandbox.js +16 -2
- package/src/lib/agent-schedule-store.js +55 -0
- package/src/lib/agent-team/team-worktree.js +28 -6
- package/src/lib/agent-tool-admission.js +104 -0
- package/src/lib/agent-worktree.js +20 -19
- package/src/lib/api-key-helper.js +10 -2
- package/src/lib/async-hook-supervisor.cjs +135 -15
- 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 +76 -11
- package/src/lib/channels/channel-manager.js +6 -0
- package/src/lib/channels/telegram-channel.js +11 -1
- package/src/lib/channels/webhook-channel.js +15 -3
- package/src/lib/checkpoint-store.js +10 -2
- 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/cloud/bundle.js +12 -2
- package/src/lib/code-review.js +32 -8
- package/src/lib/computer-use/control-backend.js +18 -3
- package/src/lib/context-breakdown.js +46 -0
- package/src/lib/context-source-ledger.js +43 -0
- package/src/lib/diff-review-follow-up.js +154 -0
- package/src/lib/doctor-checkup.js +18 -6
- package/src/lib/downloader.js +17 -3
- package/src/lib/ensure-utf8.js +11 -3
- package/src/lib/eval/tasks.js +25 -31
- package/src/lib/event-runtime-producer.js +58 -0
- package/src/lib/event-runtime-store.js +354 -0
- package/src/lib/event-runtime-worker.js +109 -0
- package/src/lib/execution-backend.js +72 -21
- package/src/lib/git-integration.js +19 -4
- package/src/lib/hook-manager.js +6 -15
- package/src/lib/hook-runner.cjs +88 -8
- package/src/lib/hook-runner.js +55 -0
- package/src/lib/hooks-v2-producers.js +15 -0
- package/src/lib/hooks-v2-runtime.js +70 -21
- package/src/lib/host-adb-bridge.js +129 -24
- package/src/lib/ide-context.js +128 -1
- package/src/lib/interaction-adapter.js +39 -0
- package/src/lib/json-schema-validate.js +190 -36
- package/src/lib/lan-pairing-preflight.js +13 -2
- 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/__tests__/lsp-server-registry.test.js +18 -0
- package/src/lib/lsp/benchmark.js +20 -4
- package/src/lib/lsp/lsp-client.js +22 -3
- package/src/lib/lsp/lsp-manager.js +4 -0
- package/src/lib/lsp/lsp-server-registry.js +11 -2
- package/src/lib/mcp-oauth.js +8 -2
- package/src/lib/npm-invocation.js +29 -0
- package/src/lib/orchestrator.js +8 -2
- package/src/lib/packer/pack-update-applier.js +14 -2
- package/src/lib/packer/pkg-runner.js +13 -2
- package/src/lib/packer/precheck.js +33 -16
- package/src/lib/packer/smoke-runner.js +18 -5
- package/src/lib/packer/web-panel-builder.js +16 -2
- package/src/lib/paths.js +1 -1
- package/src/lib/permission-rules.cjs +5 -1
- package/src/lib/personal-data-hub-aichat-wizard.js +22 -6
- package/src/lib/personal-data-hub-wiring.js +27 -3
- package/src/lib/plan-mode.js +204 -11
- package/src/lib/plugin-monitor-supervisor.js +48 -6
- package/src/lib/plugin-runtime/bin.js +45 -0
- package/src/lib/plugin-runtime/hooks.js +14 -1
- package/src/lib/plugin-runtime/install.js +11 -3
- package/src/lib/plugin-runtime/lsp.js +3 -0
- package/src/lib/plugin-runtime/mcp.js +8 -1
- package/src/lib/plugin-runtime/monitors.js +4 -0
- package/src/lib/plugin-runtime/plugin-options.js +74 -7
- package/src/lib/plugin-runtime/signature.js +45 -0
- package/src/lib/pr-create.js +21 -8
- package/src/lib/pr-link-ledger.js +15 -3
- package/src/lib/process-execution-broker/credential-agent.js +23 -3
- package/src/lib/process-execution-broker/index.js +315 -38
- package/src/lib/process-execution-broker/patch-child-process.js +2 -3
- package/src/lib/process-manager.js +18 -3
- package/src/lib/publish-workspace.js +14 -6
- package/src/lib/repl-bang-memorize.js +10 -2
- package/src/lib/repl-goal.js +19 -2
- package/src/lib/secret-store.js +159 -0
- package/src/lib/service-manager.js +64 -29
- package/src/lib/settings-hook-events.cjs +74 -9
- package/src/lib/settings-hook-events.js +20 -0
- package/src/lib/settings-hooks.cjs +4 -0
- package/src/lib/side-effect-ledger.js +22 -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/lib/sub-agent-context.js +5 -0
- package/src/lib/sub-agent-profiles.js +4 -0
- package/src/lib/turn-context.js +14 -8
- package/src/lib/unattended-action-policy.js +83 -0
- package/src/lib/update-notice.js +6 -2
- package/src/lib/with-file-lock.js +7 -0
- package/src/lib/ws-server.js +1 -0
- package/src/repl/agent-repl.js +60 -23
- package/src/repl/clipboard-copy.js +8 -2
- package/src/repl/pr-comments.js +30 -7
- package/src/runtime/agent-core.js +554 -118
- package/src/runtime/agent-runtime.js +1 -0
- package/src/runtime/coding-agent-contract-shared.cjs +52 -0
- package/src/runtime/coding-agent-policy.cjs +20 -0
- package/src/runtime/diagnostics.js +73 -8
- package/src/runtime/headless-runner.js +88 -10
- package/src/runtime/headless-stream.js +343 -21
- package/src/runtime/mcp-config.js +16 -1
- 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-Ci97vNTs.js +0 -1
- package/src/assets/web-panel/assets/Providers-zZRhNtSu.js +0 -1
- package/src/assets/web-panel/assets/Skills-D_jvQZcj.js +0 -1
- package/src/assets/web-panel/assets/Tasks-DFwxpLIk.js +0 -1
- package/src/assets/web-panel/assets/devWarning-CLOk6rdE.js +0 -1
- package/src/assets/web-panel/assets/index-CSgUfMTM.js +0 -1
- package/src/assets/web-panel/assets/index-DliSZvAP.js +0 -1
|
@@ -30,6 +30,110 @@ export const TOOL_TIER = Object.freeze({
|
|
|
30
30
|
EXTENSION: "extension",
|
|
31
31
|
});
|
|
32
32
|
|
|
33
|
+
const ADMISSION_BOOLEAN_FIELDS = Object.freeze([
|
|
34
|
+
"enforce",
|
|
35
|
+
"capabilityGranted",
|
|
36
|
+
"policyAllowed",
|
|
37
|
+
"permissionGranted",
|
|
38
|
+
"budgetOk",
|
|
39
|
+
"uiSupported",
|
|
40
|
+
]);
|
|
41
|
+
const ADMISSION_TEXT_FIELDS = Object.freeze([
|
|
42
|
+
"source",
|
|
43
|
+
"version",
|
|
44
|
+
"scope",
|
|
45
|
+
"tier",
|
|
46
|
+
]);
|
|
47
|
+
const ADMISSION_TEXT_PATTERN = /^[A-Za-z0-9@][A-Za-z0-9_.:/@+-]{0,127}$/;
|
|
48
|
+
const RESERVED_TOOL_NAMES = new Set(["__proto__", "prototype", "constructor"]);
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Parse the IDE-to-CLI CC_TOOL_ADMISSION envelope. Only decision/provenance
|
|
52
|
+
* fields are accepted; arguments, prompts and credentials have no place in
|
|
53
|
+
* this contract. Invalid input throws so a host that opted into enforcement
|
|
54
|
+
* cannot silently fall back to an ungoverned session.
|
|
55
|
+
*/
|
|
56
|
+
export function parseToolAdmissionConfig(input) {
|
|
57
|
+
if (input == null || input === "") return null;
|
|
58
|
+
let raw;
|
|
59
|
+
try {
|
|
60
|
+
raw = typeof input === "string" ? input : JSON.stringify(input);
|
|
61
|
+
} catch {
|
|
62
|
+
throw new Error("tool admission config is not valid JSON");
|
|
63
|
+
}
|
|
64
|
+
if (typeof raw !== "string") {
|
|
65
|
+
throw new Error("tool admission config must be an object");
|
|
66
|
+
}
|
|
67
|
+
if (Buffer.byteLength(raw, "utf8") > 32768) {
|
|
68
|
+
throw new Error("tool admission config exceeds 32 KiB");
|
|
69
|
+
}
|
|
70
|
+
let value;
|
|
71
|
+
try {
|
|
72
|
+
value = typeof input === "string" ? JSON.parse(input) : input;
|
|
73
|
+
} catch {
|
|
74
|
+
throw new Error("tool admission config is not valid JSON");
|
|
75
|
+
}
|
|
76
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
77
|
+
throw new Error("tool admission config must be an object");
|
|
78
|
+
}
|
|
79
|
+
if (value.enforce !== true) {
|
|
80
|
+
throw new Error("tool admission config must set enforce=true");
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
const sanitize = (entry, label) => {
|
|
84
|
+
if (!entry || typeof entry !== "object" || Array.isArray(entry)) {
|
|
85
|
+
throw new Error(`${label} must be an object`);
|
|
86
|
+
}
|
|
87
|
+
const out = {};
|
|
88
|
+
for (const field of ADMISSION_BOOLEAN_FIELDS) {
|
|
89
|
+
if (entry[field] !== undefined) {
|
|
90
|
+
if (typeof entry[field] !== "boolean") {
|
|
91
|
+
throw new Error(`${label}.${field} must be boolean`);
|
|
92
|
+
}
|
|
93
|
+
out[field] = entry[field];
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
for (const field of ADMISSION_TEXT_FIELDS) {
|
|
97
|
+
if (entry[field] !== undefined) {
|
|
98
|
+
if (
|
|
99
|
+
typeof entry[field] !== "string" ||
|
|
100
|
+
!ADMISSION_TEXT_PATTERN.test(entry[field])
|
|
101
|
+
) {
|
|
102
|
+
throw new Error(`${label}.${field} must be a safe identifier`);
|
|
103
|
+
}
|
|
104
|
+
out[field] = entry[field];
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
return out;
|
|
108
|
+
};
|
|
109
|
+
|
|
110
|
+
const config = sanitize(value, "toolAdmission");
|
|
111
|
+
config.enforce = true;
|
|
112
|
+
if (value.tools !== undefined) {
|
|
113
|
+
if (
|
|
114
|
+
!value.tools ||
|
|
115
|
+
typeof value.tools !== "object" ||
|
|
116
|
+
Array.isArray(value.tools)
|
|
117
|
+
) {
|
|
118
|
+
throw new Error("toolAdmission.tools must be an object");
|
|
119
|
+
}
|
|
120
|
+
const entries = Object.entries(value.tools);
|
|
121
|
+
if (entries.length > 256)
|
|
122
|
+
throw new Error("toolAdmission.tools exceeds 256 entries");
|
|
123
|
+
config.tools = {};
|
|
124
|
+
for (const [name, entry] of entries) {
|
|
125
|
+
if (
|
|
126
|
+
!/^[A-Za-z0-9_.:-]{1,160}$/.test(name) ||
|
|
127
|
+
RESERVED_TOOL_NAMES.has(name)
|
|
128
|
+
) {
|
|
129
|
+
throw new Error(`invalid tool admission name: ${name}`);
|
|
130
|
+
}
|
|
131
|
+
config.tools[name] = sanitize(entry, `toolAdmission.tools.${name}`);
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
return config;
|
|
135
|
+
}
|
|
136
|
+
|
|
33
137
|
/** Normalize a tier label; anything unrecognized → EXTENSION (fail-closed). */
|
|
34
138
|
export function normalizeToolTier(value) {
|
|
35
139
|
const s = String(value || "")
|
|
@@ -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 */
|
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
* rule). Cached per process (5 min) so multi-call runs don't re-spawn it.
|
|
15
15
|
*/
|
|
16
16
|
|
|
17
|
-
import {
|
|
17
|
+
import { executionBroker } from "./process-execution-broker/index.js";
|
|
18
18
|
|
|
19
19
|
const TTL_MS = 5 * 60 * 1000;
|
|
20
20
|
const _cache = new Map(); // helper command → { key, at }
|
|
@@ -31,7 +31,15 @@ export function resolveApiKeyFromHelper(helperCmd, opts = {}) {
|
|
|
31
31
|
const now = opts.now || Date.now;
|
|
32
32
|
const hit = _cache.get(cmd);
|
|
33
33
|
if (hit && now() - hit.at < TTL_MS) return hit.key;
|
|
34
|
-
const exec =
|
|
34
|
+
const exec =
|
|
35
|
+
opts.exec ||
|
|
36
|
+
((command, options) =>
|
|
37
|
+
executionBroker.execSync(command, {
|
|
38
|
+
...options,
|
|
39
|
+
origin: "credential:api-key-helper",
|
|
40
|
+
policy: "allow",
|
|
41
|
+
scope: "credential",
|
|
42
|
+
}));
|
|
35
43
|
try {
|
|
36
44
|
const out = exec(cmd, {
|
|
37
45
|
encoding: "utf-8",
|
|
@@ -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;
|
|
@@ -251,17 +359,29 @@ class AsyncHookSupervisor {
|
|
|
251
359
|
|
|
252
360
|
let child;
|
|
253
361
|
try {
|
|
254
|
-
|
|
362
|
+
const spawnOptions = {
|
|
255
363
|
cwd: opts.cwd || process.cwd(),
|
|
256
364
|
shell: true,
|
|
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
|
},
|
|
373
|
+
};
|
|
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,
|
|
265
385
|
});
|
|
266
386
|
} catch (err) {
|
|
267
387
|
this._record(hook, {
|
|
@@ -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
|
+
};
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { spawn, spawnSync } from "node:child_process";
|
|
2
1
|
import {
|
|
3
2
|
closeSync,
|
|
4
3
|
existsSync,
|
|
@@ -21,6 +20,7 @@ import {
|
|
|
21
20
|
finishAgentWorktree,
|
|
22
21
|
validateAgentWorktree,
|
|
23
22
|
} from "./agent-worktree.js";
|
|
23
|
+
import executionBroker from "./process-execution-broker/index.js";
|
|
24
24
|
|
|
25
25
|
export const DEFAULT_HEARTBEAT_INTERVAL_MS = 5000;
|
|
26
26
|
export const DEFAULT_HEARTBEAT_STALE_MS = 120000;
|
|
@@ -42,6 +42,16 @@ export const PID_IDENTITY_TOLERANCE_MS = 60000;
|
|
|
42
42
|
const START_TIME_CACHE_TTL_MS = 10000;
|
|
43
43
|
const _startTimeCache = new Map();
|
|
44
44
|
|
|
45
|
+
function runSupervisorCommand(file, args, options, origin) {
|
|
46
|
+
return _deps.spawnSync(file, args, {
|
|
47
|
+
...options,
|
|
48
|
+
origin,
|
|
49
|
+
policy: "allow",
|
|
50
|
+
scope: "background-agent",
|
|
51
|
+
shell: false,
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
|
|
45
55
|
/** CIM_DATETIME (`20260711120000.500000+480`) → epoch ms, null when unparseable. */
|
|
46
56
|
export function parseCimDateToMs(raw) {
|
|
47
57
|
const m =
|
|
@@ -77,7 +87,7 @@ function defaultReadProcessStartTimeMs(pid) {
|
|
|
77
87
|
// wmic first (fast, ~100-300ms); PowerShell CIM as fallback (wmic is
|
|
78
88
|
// removed from recent Windows 11 builds).
|
|
79
89
|
try {
|
|
80
|
-
const r =
|
|
90
|
+
const r = runSupervisorCommand(
|
|
81
91
|
"wmic",
|
|
82
92
|
[
|
|
83
93
|
"process",
|
|
@@ -88,6 +98,7 @@ function defaultReadProcessStartTimeMs(pid) {
|
|
|
88
98
|
"/value",
|
|
89
99
|
],
|
|
90
100
|
{ windowsHide: true, encoding: "utf8", timeout: 5000 },
|
|
101
|
+
"background-agent:process-start-time",
|
|
91
102
|
);
|
|
92
103
|
if (!r.error && r.status === 0) {
|
|
93
104
|
const m = /CreationDate=([^\r\n]+)/.exec(r.stdout || "");
|
|
@@ -101,10 +112,11 @@ function defaultReadProcessStartTimeMs(pid) {
|
|
|
101
112
|
const script =
|
|
102
113
|
`$p = Get-CimInstance Win32_Process -Filter 'ProcessId=${target}' -ErrorAction SilentlyContinue; ` +
|
|
103
114
|
"if ($p -and $p.CreationDate) { [DateTimeOffset]::new($p.CreationDate).ToUnixTimeMilliseconds() }";
|
|
104
|
-
const r =
|
|
115
|
+
const r = runSupervisorCommand(
|
|
105
116
|
"powershell",
|
|
106
117
|
["-NoProfile", "-NonInteractive", "-Command", script],
|
|
107
118
|
{ windowsHide: true, encoding: "utf8", timeout: 10000 },
|
|
119
|
+
"background-agent:process-start-time",
|
|
108
120
|
);
|
|
109
121
|
if (!r.error && r.status === 0) {
|
|
110
122
|
const n = Number(String(r.stdout || "").trim());
|
|
@@ -118,10 +130,15 @@ function defaultReadProcessStartTimeMs(pid) {
|
|
|
118
130
|
// POSIX: `ps -o lstart=` gives an absolute start timestamp on Linux + macOS
|
|
119
131
|
// (procps and BSD ps both support it; busybox ps does not → null → open).
|
|
120
132
|
try {
|
|
121
|
-
const r =
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
133
|
+
const r = runSupervisorCommand(
|
|
134
|
+
"ps",
|
|
135
|
+
["-o", "lstart=", "-p", String(target)],
|
|
136
|
+
{
|
|
137
|
+
encoding: "utf8",
|
|
138
|
+
timeout: 5000,
|
|
139
|
+
},
|
|
140
|
+
"background-agent:process-start-time",
|
|
141
|
+
);
|
|
125
142
|
if (!r.error && r.status === 0) {
|
|
126
143
|
const t = Date.parse(String(r.stdout || "").trim());
|
|
127
144
|
if (Number.isFinite(t) && t > 0) return t;
|
|
@@ -147,13 +164,14 @@ function defaultKillProcessTree(pid, signal = "SIGKILL") {
|
|
|
147
164
|
if (target === process.pid) return false;
|
|
148
165
|
try {
|
|
149
166
|
if (process.platform === "win32") {
|
|
150
|
-
const r =
|
|
167
|
+
const r = runSupervisorCommand(
|
|
151
168
|
"taskkill",
|
|
152
169
|
["/PID", String(target), "/T", "/F"],
|
|
153
170
|
{
|
|
154
171
|
windowsHide: true,
|
|
155
172
|
encoding: "utf8",
|
|
156
173
|
},
|
|
174
|
+
"background-agent:process-tree-kill",
|
|
157
175
|
);
|
|
158
176
|
return !r.error && r.status === 0;
|
|
159
177
|
}
|
|
@@ -170,8 +188,8 @@ function defaultKillProcessTree(pid, signal = "SIGKILL") {
|
|
|
170
188
|
}
|
|
171
189
|
|
|
172
190
|
export const _deps = {
|
|
173
|
-
spawn,
|
|
174
|
-
spawnSync,
|
|
191
|
+
spawn: executionBroker.spawn.bind(executionBroker),
|
|
192
|
+
spawnSync: executionBroker.spawnSync.bind(executionBroker),
|
|
175
193
|
// Single POSIX signal seam: every kill this module issues goes through here
|
|
176
194
|
// so tests can stub it — a bare `process.kill` is NOT interceptable and a
|
|
177
195
|
// fixture recording a live pid would eat a REAL signal (the shard-2/4
|
|
@@ -741,7 +759,7 @@ export function resumeBackgroundAgent(id, prompt, options = {}) {
|
|
|
741
759
|
}
|
|
742
760
|
|
|
743
761
|
/**
|
|
744
|
-
* Fail fast on an unusable cwd. Without this,
|
|
762
|
+
* Fail fast on an unusable cwd. Without this, process launch surfaces a bad cwd as
|
|
745
763
|
* an ASYNC 'error' event on the detached child — which nothing listened to
|
|
746
764
|
* (uncaught exception with no context) — and the pre-written state/job files
|
|
747
765
|
* stayed behind as a phantom "running" session. Deleted, file-replaced, and
|
|
@@ -826,6 +844,10 @@ export function launchBackgroundAgent({
|
|
|
826
844
|
detached: true,
|
|
827
845
|
stdio: "ignore",
|
|
828
846
|
windowsHide: true,
|
|
847
|
+
origin: "background-agent:worker",
|
|
848
|
+
policy: "allow",
|
|
849
|
+
scope: "background-agent",
|
|
850
|
+
shell: false,
|
|
829
851
|
});
|
|
830
852
|
} catch (error) {
|
|
831
853
|
rmSync(jobFile, { force: true });
|
|
@@ -922,13 +944,14 @@ export function stopBackgroundAgent(id) {
|
|
|
922
944
|
return { ...lost, stopped: false };
|
|
923
945
|
}
|
|
924
946
|
if (process.platform === "win32") {
|
|
925
|
-
const killed =
|
|
947
|
+
const killed = runSupervisorCommand(
|
|
926
948
|
"taskkill",
|
|
927
949
|
["/PID", String(state.pid), "/T", "/F"],
|
|
928
950
|
{
|
|
929
951
|
windowsHide: true,
|
|
930
952
|
encoding: "utf8",
|
|
931
953
|
},
|
|
954
|
+
"background-agent:stop-tree",
|
|
932
955
|
);
|
|
933
956
|
if (killed.error || (killed.status !== 0 && isProcessAlive(state.pid))) {
|
|
934
957
|
throw new Error(
|