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
|
@@ -8,17 +8,19 @@
|
|
|
8
8
|
* thin re-export shim for backwards compatibility.
|
|
9
9
|
*/
|
|
10
10
|
|
|
11
|
-
import {
|
|
11
|
+
import { executionBroker } from "../lib/process-execution-broker/index.js";
|
|
12
|
+
import { AsyncLocalStorage } from "node:async_hooks";
|
|
12
13
|
import { EventEmitter } from "events";
|
|
13
14
|
import { pathToFileURL } from "url";
|
|
14
15
|
import { safeJsonParse } from "../lib/safe-json.js";
|
|
16
|
+
import { EventRuntimeProducer } from "../lib/event-runtime-producer.js";
|
|
15
17
|
|
|
16
18
|
/**
|
|
17
19
|
* Injectable dependencies — overridable from tests.
|
|
18
20
|
* `fetch` defaults to the global fetch (Node 18+).
|
|
19
21
|
*/
|
|
20
22
|
export const _deps = {
|
|
21
|
-
spawn,
|
|
23
|
+
spawn: executionBroker.spawn.bind(executionBroker),
|
|
22
24
|
fetch: (...args) => globalThis.fetch(...args),
|
|
23
25
|
// Backoff sleep seam (tests override with a no-op so retries don't wait).
|
|
24
26
|
sleep: (ms) => new Promise((resolve) => setTimeout(resolve, ms)),
|
|
@@ -166,6 +168,178 @@ export class MCPClient extends EventEmitter {
|
|
|
166
168
|
options && Array.isArray(options.roots) && options.roots.length > 0
|
|
167
169
|
? options.roots.map(String)
|
|
168
170
|
: null;
|
|
171
|
+
// MCP elicitation (server -> client `elicitation/create`). The handler is
|
|
172
|
+
// intentionally injected by the host (REPL, headless, or Desktop bridge)
|
|
173
|
+
// so the transport layer never reads stdin or invents an approval policy.
|
|
174
|
+
this._elicitationHandler =
|
|
175
|
+
typeof options.elicitationHandler === "function"
|
|
176
|
+
? options.elicitationHandler
|
|
177
|
+
: null;
|
|
178
|
+
this._elicitationHandlers = new Map();
|
|
179
|
+
this._elicitationContext = new AsyncLocalStorage();
|
|
180
|
+
this._elicitationTimeoutMs =
|
|
181
|
+
Number(options.elicitationTimeoutMs) > 0
|
|
182
|
+
? Number(options.elicitationTimeoutMs)
|
|
183
|
+
: 180000;
|
|
184
|
+
this._pendingElicitations = new Map();
|
|
185
|
+
this._runtimeProducer = options.eventRuntimeStore
|
|
186
|
+
? new EventRuntimeProducer({ store: options.eventRuntimeStore, emitter: this })
|
|
187
|
+
: null;
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
/** Install or clear the host-side MCP elicitation resolver. */
|
|
191
|
+
setElicitationHandler(handler, options = {}) {
|
|
192
|
+
const sessionId = options.sessionId;
|
|
193
|
+
if (sessionId != null && sessionId !== "") {
|
|
194
|
+
const key = String(sessionId);
|
|
195
|
+
if (typeof handler === "function") this._elicitationHandlers.set(key, handler);
|
|
196
|
+
else this._elicitationHandlers.delete(key);
|
|
197
|
+
} else {
|
|
198
|
+
this._elicitationHandler =
|
|
199
|
+
typeof handler === "function" ? handler : null;
|
|
200
|
+
}
|
|
201
|
+
if (Number(options.timeoutMs) > 0) {
|
|
202
|
+
this._elicitationTimeoutMs = Number(options.timeoutMs);
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
/** Run an async agent turn with a session-scoped elicitation route. */
|
|
207
|
+
withElicitationContext(sessionId, fn) {
|
|
208
|
+
if (typeof fn !== "function") return Promise.resolve(undefined);
|
|
209
|
+
return this._elicitationContext.run(
|
|
210
|
+
sessionId == null ? null : String(sessionId),
|
|
211
|
+
fn,
|
|
212
|
+
);
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
/** Remove a session route when a WS session is closed. */
|
|
216
|
+
clearElicitationHandler(sessionId) {
|
|
217
|
+
if (sessionId == null || sessionId === "") return false;
|
|
218
|
+
return this._elicitationHandlers.delete(String(sessionId));
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
/** Resolve a pending server elicitation by its server/request id pair. */
|
|
222
|
+
respondElicitation(serverName, requestId, response) {
|
|
223
|
+
const key = this._elicitationKey(serverName, requestId);
|
|
224
|
+
const pending = this._pendingElicitations.get(key);
|
|
225
|
+
if (!pending) return false;
|
|
226
|
+
pending.resolve(this._normalizeElicitationResponse(response));
|
|
227
|
+
try {
|
|
228
|
+
this._runtimeProducer?.store.acknowledgeInbox(
|
|
229
|
+
`mcp-elicitation:${this._elicitationKey(serverName, requestId)}`,
|
|
230
|
+
{ response: this._normalizeElicitationResponse(response) },
|
|
231
|
+
);
|
|
232
|
+
} catch {}
|
|
233
|
+
return true;
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
/** Cancel a pending server elicitation. */
|
|
237
|
+
cancelElicitation(serverName, requestId) {
|
|
238
|
+
return this.respondElicitation(serverName, requestId, {
|
|
239
|
+
action: "cancel",
|
|
240
|
+
});
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
_elicitationKey(serverName, requestId) {
|
|
244
|
+
return `${String(serverName)}:${String(requestId)}`;
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
_normalizeElicitationResponse(response) {
|
|
248
|
+
const action = String(response?.action || "decline").toLowerCase();
|
|
249
|
+
const safeAction = new Set(["accept", "decline", "cancel"]).has(action)
|
|
250
|
+
? action
|
|
251
|
+
: "decline";
|
|
252
|
+
const normalized = { action: safeAction };
|
|
253
|
+
if (safeAction === "accept" && response?.content !== undefined) {
|
|
254
|
+
normalized.content = response.content;
|
|
255
|
+
}
|
|
256
|
+
return normalized;
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
async _resolveElicitation(serverName, requestId, params = {}) {
|
|
260
|
+
const key = this._elicitationKey(serverName, requestId);
|
|
261
|
+
try {
|
|
262
|
+
this._runtimeProducer?.publish(
|
|
263
|
+
{ type: "mcp_elicitation", server: serverName, requestId, params },
|
|
264
|
+
{ origin: "mcp", id: `mcp-elicitation:${key}` },
|
|
265
|
+
);
|
|
266
|
+
} catch (error) {
|
|
267
|
+
this.emit("elicitation-error", { server: serverName, requestId, error });
|
|
268
|
+
return { action: "cancel" };
|
|
269
|
+
}
|
|
270
|
+
const contextSessionId = this._elicitationContext.getStore();
|
|
271
|
+
const handler =
|
|
272
|
+
(contextSessionId && this._elicitationHandlers.get(contextSessionId)) ||
|
|
273
|
+
this._elicitationHandler;
|
|
274
|
+
if (handler) {
|
|
275
|
+
try {
|
|
276
|
+
const direct = await handler({
|
|
277
|
+
server: serverName,
|
|
278
|
+
requestId,
|
|
279
|
+
...params,
|
|
280
|
+
});
|
|
281
|
+
if (direct !== undefined) {
|
|
282
|
+
const response = this._normalizeElicitationResponse(direct);
|
|
283
|
+
try {
|
|
284
|
+
this._runtimeProducer?.store.acknowledgeInbox(
|
|
285
|
+
`mcp-elicitation:${key}`,
|
|
286
|
+
{ response },
|
|
287
|
+
);
|
|
288
|
+
} catch {}
|
|
289
|
+
return response;
|
|
290
|
+
}
|
|
291
|
+
} catch (error) {
|
|
292
|
+
this.emit("elicitation-error", {
|
|
293
|
+
server: serverName,
|
|
294
|
+
requestId,
|
|
295
|
+
error,
|
|
296
|
+
});
|
|
297
|
+
return { action: "cancel" };
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
// An event-driven host may answer later through respondElicitation().
|
|
302
|
+
// No handler/listener means fail closed with decline rather than leaving a
|
|
303
|
+
// server request hanging indefinitely.
|
|
304
|
+
if (this.listenerCount("elicitation-request") === 0) {
|
|
305
|
+
try {
|
|
306
|
+
this._runtimeProducer?.store.acknowledgeInbox(
|
|
307
|
+
`mcp-elicitation:${key}`,
|
|
308
|
+
{ response: { action: "decline" } },
|
|
309
|
+
);
|
|
310
|
+
} catch {}
|
|
311
|
+
return { action: "decline" };
|
|
312
|
+
}
|
|
313
|
+
return new Promise((resolve) => {
|
|
314
|
+
const timeout = setTimeout(() => {
|
|
315
|
+
this._pendingElicitations.delete(key);
|
|
316
|
+
this.emit("elicitation-timeout", { server: serverName, requestId });
|
|
317
|
+
try {
|
|
318
|
+
this._runtimeProducer?.store.fail(
|
|
319
|
+
"inbox",
|
|
320
|
+
`mcp-elicitation:${key}`,
|
|
321
|
+
"elicitation timeout",
|
|
322
|
+
{ retryDelayMs: 0, maxAttempts: 1 },
|
|
323
|
+
);
|
|
324
|
+
} catch {}
|
|
325
|
+
resolve({ action: "cancel" });
|
|
326
|
+
}, this._elicitationTimeoutMs);
|
|
327
|
+
timeout.unref?.();
|
|
328
|
+
this._pendingElicitations.set(key, {
|
|
329
|
+
resolve: (response) => {
|
|
330
|
+
clearTimeout(timeout);
|
|
331
|
+
this._pendingElicitations.delete(key);
|
|
332
|
+
resolve(this._normalizeElicitationResponse(response));
|
|
333
|
+
},
|
|
334
|
+
});
|
|
335
|
+
this.emit("elicitation-request", {
|
|
336
|
+
server: serverName,
|
|
337
|
+
requestId,
|
|
338
|
+
...params,
|
|
339
|
+
respond: (response) => this.respondElicitation(serverName, requestId, response),
|
|
340
|
+
cancel: () => this.cancelElicitation(serverName, requestId),
|
|
341
|
+
});
|
|
342
|
+
});
|
|
169
343
|
}
|
|
170
344
|
|
|
171
345
|
/**
|
|
@@ -320,6 +494,13 @@ export class MCPClient extends EventEmitter {
|
|
|
320
494
|
...this._agentIdentityEnv(),
|
|
321
495
|
...(config.env || {}),
|
|
322
496
|
},
|
|
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,
|
|
323
504
|
});
|
|
324
505
|
|
|
325
506
|
entry.process = proc;
|
|
@@ -510,6 +691,12 @@ export class MCPClient extends EventEmitter {
|
|
|
510
691
|
const entry = this.servers.get(name);
|
|
511
692
|
if (!entry) return false;
|
|
512
693
|
|
|
694
|
+
for (const [key, pending] of this._pendingElicitations) {
|
|
695
|
+
if (key.startsWith(`${String(name)}:`)) {
|
|
696
|
+
pending.resolve({ action: "cancel" });
|
|
697
|
+
}
|
|
698
|
+
}
|
|
699
|
+
|
|
513
700
|
if (entry.process) {
|
|
514
701
|
entry.process.kill();
|
|
515
702
|
}
|
|
@@ -1125,6 +1312,12 @@ export class MCPClient extends EventEmitter {
|
|
|
1125
1312
|
this._sendResponse(serverName, id, {});
|
|
1126
1313
|
return;
|
|
1127
1314
|
}
|
|
1315
|
+
if (method === "elicitation/create") {
|
|
1316
|
+
this._resolveElicitation(serverName, id, msg.params || {})
|
|
1317
|
+
.then((response) => this._sendResponse(serverName, id, response))
|
|
1318
|
+
.catch(() => this._sendResponse(serverName, id, { action: "cancel" }));
|
|
1319
|
+
return;
|
|
1320
|
+
}
|
|
1128
1321
|
this._sendResponse(serverName, id, undefined, {
|
|
1129
1322
|
code: -32601,
|
|
1130
1323
|
message: `method not found: ${method}`,
|
|
@@ -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;
|
|
@@ -91,6 +91,21 @@ describe("createVendorAdapterBridge (cli)", () => {
|
|
|
91
91
|
expect(r.userId).toBe("userToken");
|
|
92
92
|
});
|
|
93
93
|
|
|
94
|
+
it("attaches a validated session to the registry runtime adapter", async () => {
|
|
95
|
+
const runtimeAdapter = { setSession: vi.fn() };
|
|
96
|
+
const bridge = createVendorAdapterBridge({
|
|
97
|
+
specs: [fakeSpec("deepseek", async () => ({ ok: true }))],
|
|
98
|
+
runtimeAdapter,
|
|
99
|
+
_httpClientFactory: () => ({}),
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
await bridge.registerVendor("deepseek", { userToken: "abc" });
|
|
103
|
+
|
|
104
|
+
expect(runtimeAdapter.setSession).toHaveBeenCalledOnce();
|
|
105
|
+
expect(runtimeAdapter.setSession.mock.calls[0][0]).toBe("deepseek");
|
|
106
|
+
expect(runtimeAdapter.setSession.mock.calls[0][1].get("userToken")).toBe("abc");
|
|
107
|
+
});
|
|
108
|
+
|
|
94
109
|
it("UNKNOWN_VENDOR for unrecognized", async () => {
|
|
95
110
|
const bridge = createVendorAdapterBridge({
|
|
96
111
|
specs: [fakeSpec("deepseek", async () => ({ ok: true }))],
|
|
@@ -281,6 +281,8 @@ export const ROLE_TOOL_WHITELIST = {
|
|
|
281
281
|
"read_file",
|
|
282
282
|
"write_file",
|
|
283
283
|
"edit_file",
|
|
284
|
+
"delete_file",
|
|
285
|
+
"move_file",
|
|
284
286
|
"run_shell",
|
|
285
287
|
"search_files",
|
|
286
288
|
"list_dir",
|
|
@@ -297,6 +299,8 @@ export const ROLE_TOOL_WHITELIST = {
|
|
|
297
299
|
"read_file",
|
|
298
300
|
"write_file",
|
|
299
301
|
"edit_file",
|
|
302
|
+
"delete_file",
|
|
303
|
+
"move_file",
|
|
300
304
|
"run_shell",
|
|
301
305
|
"search_files",
|
|
302
306
|
"list_dir",
|
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
|
@@ -11,6 +11,13 @@
|
|
|
11
11
|
|
|
12
12
|
import { EventEmitter } from "node:events";
|
|
13
13
|
import crypto from "node:crypto";
|
|
14
|
+
import { EventRuntimeProducer } from "./event-runtime-producer.js";
|
|
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
|
+
};
|
|
14
21
|
|
|
15
22
|
/**
|
|
16
23
|
* @typedef {Object} InteractionRequest
|
|
@@ -27,8 +34,11 @@ import crypto from "node:crypto";
|
|
|
27
34
|
*/
|
|
28
35
|
|
|
29
36
|
class AgentIPCBus extends EventEmitter {
|
|
30
|
-
constructor() {
|
|
37
|
+
constructor({ runtimeStore = null } = {}) {
|
|
31
38
|
super();
|
|
39
|
+
this._runtimeProducer = runtimeStore
|
|
40
|
+
? new EventRuntimeProducer({ store: runtimeStore, emitter: this })
|
|
41
|
+
: null;
|
|
32
42
|
/** @type {Map<string, {req: InteractionRequest, resolve: Function, reject: Function, timer?: NodeJS.Timeout}>} */
|
|
33
43
|
this._pendingRequests = new Map();
|
|
34
44
|
/** @type {Map<string, Function>} resolvers by agentId */
|
|
@@ -58,6 +68,10 @@ class AgentIPCBus extends EventEmitter {
|
|
|
58
68
|
this.emit("agent:unregistered", { agentId });
|
|
59
69
|
}
|
|
60
70
|
|
|
71
|
+
isAgentRegistered(agentId) {
|
|
72
|
+
return this._agentResolvers.has(agentId);
|
|
73
|
+
}
|
|
74
|
+
|
|
61
75
|
/**
|
|
62
76
|
* Child agent calls this to request human interaction
|
|
63
77
|
* @param {string} agentId
|
|
@@ -91,6 +105,19 @@ class AgentIPCBus extends EventEmitter {
|
|
|
91
105
|
}, fullReq.timeoutMs);
|
|
92
106
|
|
|
93
107
|
this._pendingRequests.set(requestId, { req: fullReq, resolve, reject, timer });
|
|
108
|
+
try {
|
|
109
|
+
this._runtimeProducer?.publish(
|
|
110
|
+
{ type: "interaction_request", request: fullReq },
|
|
111
|
+
{ origin: "agent-ipc", id: `interaction:${requestId}` },
|
|
112
|
+
);
|
|
113
|
+
} catch (error) {
|
|
114
|
+
// Durable publication is fail-closed for configured runtime mode: do
|
|
115
|
+
// not leave a request that cannot be recovered after a crash.
|
|
116
|
+
this._pendingRequests.delete(requestId);
|
|
117
|
+
clearTimeout(timer);
|
|
118
|
+
reject(error);
|
|
119
|
+
return;
|
|
120
|
+
}
|
|
94
121
|
this.emit("request", fullReq);
|
|
95
122
|
});
|
|
96
123
|
}
|
|
@@ -106,6 +133,10 @@ class AgentIPCBus extends EventEmitter {
|
|
|
106
133
|
if (entry.timer) clearTimeout(entry.timer);
|
|
107
134
|
this._pendingRequests.delete(requestId);
|
|
108
135
|
entry.resolve(response);
|
|
136
|
+
try {
|
|
137
|
+
const id = `interaction:${requestId}`;
|
|
138
|
+
this._runtimeProducer?.store.acknowledgeInbox(id, { response });
|
|
139
|
+
} catch {}
|
|
109
140
|
this.emit("request:resolved", { requestId, response });
|
|
110
141
|
return true;
|
|
111
142
|
}
|
|
@@ -144,12 +175,12 @@ class AgentIPCBus extends EventEmitter {
|
|
|
144
175
|
* @returns {Promise<{process: ChildProcess, agentId: string}>}
|
|
145
176
|
*/
|
|
146
177
|
async spawnAgentProcess(command, args = [], options = {}) {
|
|
147
|
-
const { spawn } = await import("node:child_process");
|
|
148
178
|
const agentId = options.agentId || crypto.randomUUID();
|
|
149
179
|
const heartbeatMs = options.heartbeatMs || 30000;
|
|
150
180
|
|
|
151
181
|
return new Promise((resolve, reject) => {
|
|
152
|
-
const
|
|
182
|
+
const spawnOptions = options.spawnOptions || {};
|
|
183
|
+
const child = _deps.spawn(command, args, {
|
|
153
184
|
stdio: ["pipe", "pipe", "pipe"],
|
|
154
185
|
env: {
|
|
155
186
|
...process.env,
|
|
@@ -158,7 +189,11 @@ class AgentIPCBus extends EventEmitter {
|
|
|
158
189
|
CHAINLESSCHAIN_IPC_PROTOCOL: "jsonrpc-stdio-v1",
|
|
159
190
|
...options.env,
|
|
160
191
|
},
|
|
161
|
-
...
|
|
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,
|
|
162
197
|
});
|
|
163
198
|
|
|
164
199
|
let buffer = "";
|
|
@@ -326,6 +361,10 @@ class AgentIPCBus extends EventEmitter {
|
|
|
326
361
|
}
|
|
327
362
|
|
|
328
363
|
// Singleton
|
|
329
|
-
const ipcBus = new AgentIPCBus(
|
|
364
|
+
const ipcBus = new AgentIPCBus(
|
|
365
|
+
process.env.CC_EVENT_RUNTIME_DURABLE === "1"
|
|
366
|
+
? { runtimeStore: new EventRuntimeStore() }
|
|
367
|
+
: {},
|
|
368
|
+
);
|
|
330
369
|
export default ipcBus;
|
|
331
370
|
export { AgentIPCBus };
|
package/src/lib/agent-sandbox.js
CHANGED
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
/** OS-isolated shell execution for the coding agent. */
|
|
2
|
-
import { spawnSync } from "node:child_process";
|
|
3
2
|
import path from "node:path";
|
|
4
3
|
import { proxyEnv } from "./sandbox-egress-proxy.js";
|
|
4
|
+
import executionBroker from "./process-execution-broker/index.js";
|
|
5
5
|
|
|
6
6
|
export const DEFAULT_SANDBOX_IMAGE = "node:22-bookworm-slim";
|
|
7
|
-
export const _deps = {
|
|
7
|
+
export const _deps = {
|
|
8
|
+
spawnSync: (...args) => executionBroker.spawnSync(...args),
|
|
9
|
+
};
|
|
8
10
|
|
|
9
11
|
function stringList(value) {
|
|
10
12
|
if (!Array.isArray(value)) return [];
|
|
@@ -126,6 +128,10 @@ export function executeSandboxedShell(command, sandbox, options = {}) {
|
|
|
126
128
|
}
|
|
127
129
|
args.push(sandbox.image, "sh", "-lc", String(command || ""));
|
|
128
130
|
const result = _deps.spawnSync("docker", args, {
|
|
131
|
+
origin: "agent-sandbox:docker",
|
|
132
|
+
scope: "sandbox",
|
|
133
|
+
policy: "allow",
|
|
134
|
+
shell: false,
|
|
129
135
|
encoding: "utf8",
|
|
130
136
|
timeout: options.timeout,
|
|
131
137
|
maxBuffer: options.maxBuffer,
|
|
@@ -189,6 +195,10 @@ function executeBubblewrapShell(command, sandbox, options, hostCwd, policy) {
|
|
|
189
195
|
? { ...(options.env || {}), ...proxyEnv(egress.port, "127.0.0.1") }
|
|
190
196
|
: options.env;
|
|
191
197
|
const result = _deps.spawnSync("bwrap", args, {
|
|
198
|
+
origin: "agent-sandbox:bubblewrap",
|
|
199
|
+
scope: "sandbox",
|
|
200
|
+
policy: "allow",
|
|
201
|
+
shell: false,
|
|
192
202
|
cwd: hostCwd,
|
|
193
203
|
env: bwrapEnv,
|
|
194
204
|
encoding: "utf8",
|
|
@@ -244,6 +254,10 @@ export function probeSandboxAvailability(sandbox, deps = _deps) {
|
|
|
244
254
|
? ["bwrap", ["--version"]]
|
|
245
255
|
: ["docker", ["version", "--format", "{{.Server.Version}}"]];
|
|
246
256
|
const result = deps.spawnSync(probeArgs[0], probeArgs[1], {
|
|
257
|
+
origin: "agent-sandbox:probe",
|
|
258
|
+
scope: "sandbox",
|
|
259
|
+
policy: "allow",
|
|
260
|
+
shell: false,
|
|
247
261
|
encoding: "utf8",
|
|
248
262
|
timeout: 10000,
|
|
249
263
|
windowsHide: true,
|
|
@@ -27,6 +27,7 @@ import { isEntryExpired, effectiveFireAt } from "./schedule-planner.js";
|
|
|
27
27
|
import { SUBAGENT_PERMISSION_MODES } from "./subagent-contract.js";
|
|
28
28
|
import { parseGoalCondition } from "./goal-condition-engine.js";
|
|
29
29
|
import { normalizeActionClass } from "./unattended-action-policy.js";
|
|
30
|
+
import { withFileLock } from "./with-file-lock.js";
|
|
30
31
|
|
|
31
32
|
export const SCHEDULE_KINDS = Object.freeze(["wakeup", "cron", "monitor"]);
|
|
32
33
|
|
|
@@ -155,6 +156,10 @@ function isSchedulableStatus(entry) {
|
|
|
155
156
|
: entry.status === "active";
|
|
156
157
|
}
|
|
157
158
|
|
|
159
|
+
function hasLiveExecutionLease(entry, now) {
|
|
160
|
+
return Number(entry.executionLease?.expiresAt) > Number(now);
|
|
161
|
+
}
|
|
162
|
+
|
|
158
163
|
/** Statuses an entry can never leave — safe to prune. */
|
|
159
164
|
export const TERMINAL_STATUSES = Object.freeze([
|
|
160
165
|
"fired",
|
|
@@ -487,6 +492,7 @@ export class AgentScheduleStore {
|
|
|
487
492
|
// (defense-in-depth: retireExpired need not have run first).
|
|
488
493
|
if (isEntryExpired(entry, now)) return false;
|
|
489
494
|
if (!isSchedulableStatus(entry)) return false;
|
|
495
|
+
if (hasLiveExecutionLease(entry, now)) return false;
|
|
490
496
|
// The effective fire time applies the entry's OWN deterministic jitter
|
|
491
497
|
// (`entry.jitterMs`, default 0 → base fire time, byte-identical to the
|
|
492
498
|
// prior `dueAt`/`nextAt` comparison). A jittered entry fires slightly
|
|
@@ -496,6 +502,52 @@ export class AgentScheduleStore {
|
|
|
496
502
|
});
|
|
497
503
|
}
|
|
498
504
|
|
|
505
|
+
/**
|
|
506
|
+
* Atomically claim due entries for one agenda runner. The lease is retained
|
|
507
|
+
* on the durable entry, so a second process cannot fire the same item while
|
|
508
|
+
* the first is working; an abandoned process becomes reclaimable after the
|
|
509
|
+
* lease expires. This is deliberately separate from `due()` for backwards
|
|
510
|
+
* compatibility with read-only callers and injected test stores.
|
|
511
|
+
*/
|
|
512
|
+
claimDue(atMs = null, leaseMs = 120000) {
|
|
513
|
+
const now = atMs != null ? Number(atMs) : this._now();
|
|
514
|
+
const ttl = Math.max(1000, Number(leaseMs) || 120000);
|
|
515
|
+
this._ensureDir();
|
|
516
|
+
return withFileLock(path.join(this.dir, ".agenda-claims"), () => {
|
|
517
|
+
const claimed = [];
|
|
518
|
+
for (const kind of SCHEDULE_KINDS) {
|
|
519
|
+
const entries = this._readAll(kind);
|
|
520
|
+
let changed = false;
|
|
521
|
+
for (const entry of entries) {
|
|
522
|
+
if (!isSchedulableStatus(entry) || isEntryExpired(entry, now)) continue;
|
|
523
|
+
if (hasLiveExecutionLease(entry, now)) continue;
|
|
524
|
+
const fireAt = effectiveFireAt(entry);
|
|
525
|
+
if (fireAt == null || fireAt > now) continue;
|
|
526
|
+
entry.executionLease = {
|
|
527
|
+
owner: `${process.pid}:${randomUUID()}`,
|
|
528
|
+
claimedAt: now,
|
|
529
|
+
expiresAt: now + ttl,
|
|
530
|
+
};
|
|
531
|
+
claimed.push({ ...entry, executionLease: { ...entry.executionLease } });
|
|
532
|
+
changed = true;
|
|
533
|
+
}
|
|
534
|
+
if (changed) this._writeAll(kind, entries);
|
|
535
|
+
}
|
|
536
|
+
return claimed;
|
|
537
|
+
}, { failIfUnavailable: true });
|
|
538
|
+
}
|
|
539
|
+
|
|
540
|
+
/** Release a failed/aborted claim so a later runner can retry it. */
|
|
541
|
+
releaseClaim(id) {
|
|
542
|
+
for (const kind of SCHEDULE_KINDS) {
|
|
543
|
+
const updated = this._mutate(kind, id, (entry) => {
|
|
544
|
+
delete entry.executionLease;
|
|
545
|
+
});
|
|
546
|
+
if (updated) return updated;
|
|
547
|
+
}
|
|
548
|
+
return null;
|
|
549
|
+
}
|
|
550
|
+
|
|
499
551
|
/**
|
|
500
552
|
* Retire every schedulable entry whose `expiresAt` has passed (status →
|
|
501
553
|
* "expired", stamping `expiredAt`), across all kinds, so it never fires again.
|
|
@@ -632,6 +684,9 @@ export class AgentScheduleStore {
|
|
|
632
684
|
const entry = entries.find((e) => e.id === id);
|
|
633
685
|
if (!entry) return null;
|
|
634
686
|
fn(entry);
|
|
687
|
+
// Every lifecycle mutation settles the claim before the write becomes
|
|
688
|
+
// visible, including a recurring cron/monitor entry that remains active.
|
|
689
|
+
delete entry.executionLease;
|
|
635
690
|
this._writeAll(kind, entries);
|
|
636
691
|
return entry;
|
|
637
692
|
}
|
|
@@ -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 {
|