chainlesschain 0.162.174 → 0.162.176
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-Dcl-X1Hw.js → AIOps-BDLu5Elz.js} +1 -1
- package/src/assets/web-panel/assets/{ActionButton-ClMh9HzT.js → ActionButton-DH5us-CK.js} +1 -1
- package/src/assets/web-panel/assets/{Analytics-DPj0rSQ5.js → Analytics-BZ0u8IFG.js} +3 -3
- package/src/assets/web-panel/assets/AppLayout-Duu8xR14.js +9 -0
- package/src/assets/web-panel/assets/{Artifacts-L8781rE8.js → Artifacts-Br2YBbh_.js} +1 -1
- package/src/assets/web-panel/assets/{Audit-CffB6WIP.js → Audit-C3C6a482.js} +1 -1
- package/src/assets/web-panel/assets/{BackgroundAgents-BkLhayyY.js → BackgroundAgents-CzHvebcj.js} +1 -1
- package/src/assets/web-panel/assets/{Backup-BQnwFI78.js → Backup-DZNHYDFd.js} +1 -1
- package/src/assets/web-panel/assets/{BaseInput-xuUGHQch.js → BaseInput-VRiC4fIx.js} +1 -1
- package/src/assets/web-panel/assets/{Chat-feQXwWrg.js → Chat-Cm08WWAD.js} +5 -5
- package/src/assets/web-panel/assets/ChatBubbleRenderer-CGHYrugQ.js +1 -0
- package/src/assets/web-panel/assets/{Checkbox-DVaIiUGW.js → Checkbox-DtByk7d7.js} +1 -1
- package/src/assets/web-panel/assets/{Codegen-D2kEnzeU.js → Codegen-CphXXOfD.js} +1 -1
- package/src/assets/web-panel/assets/{Col-azDoyusS.js → Col-Hz86TnLR.js} +1 -1
- package/src/assets/web-panel/assets/{Community-OZdnPWM5.js → Community-HP03ltpD.js} +1 -1
- package/src/assets/web-panel/assets/{Compact-VDMvBN-C.js → Compact-B_N1mqQz.js} +1 -1
- package/src/assets/web-panel/assets/{Compliance-CPJ_zgQ1.js → Compliance-CVztRpi8.js} +1 -1
- package/src/assets/web-panel/assets/{Cowork-iRBwcsOA.js → Cowork-wUVOyZih.js} +4 -4
- package/src/assets/web-panel/assets/{Cron-DrxrspUz.js → Cron-DW5TLJKw.js} +2 -2
- package/src/assets/web-panel/assets/{Crosschain-D-rkAZXF.js → Crosschain-CrAFGhWH.js} +1 -1
- package/src/assets/web-panel/assets/{DID-5Pp1Xnmr.js → DID-pA-oD8ke.js} +2 -2
- package/src/assets/web-panel/assets/Dashboard-CXrd5gAw.js +3 -0
- package/src/assets/web-panel/assets/{Dropdown-BWj_E0Ie.js → Dropdown-EweJcsek.js} +1 -1
- package/src/assets/web-panel/assets/{EmailListRenderer-BT-wgSEh.js → EmailListRenderer-C9xVLPG4.js} +1 -1
- package/src/assets/web-panel/assets/{FamilyGuardDashboard-1PzH23pa.js → FamilyGuardDashboard-CDcEEqlu.js} +1 -1
- package/src/assets/web-panel/assets/{Federation-BxMIXGC6.js → Federation-DtZda9A_.js} +1 -1
- package/src/assets/web-panel/assets/{FormItemContext-CyJobBln.js → FormItemContext-CiYdcVz3.js} +1 -1
- package/src/assets/web-panel/assets/{GenericCardRenderer-YkjFuQTN.js → GenericCardRenderer-DA13uwA0.js} +1 -1
- package/src/assets/web-panel/assets/{Git-C2a98Wp_.js → Git-DzRhZD1O.js} +2 -2
- package/src/assets/web-panel/assets/{Governance-DKL3Fl84.js → Governance-D5RMgOSb.js} +1 -1
- package/src/assets/web-panel/assets/{Inference-BJ2cQC_3.js → Inference-BgrPfSNk.js} +1 -1
- package/src/assets/web-panel/assets/{KnowledgeGraph-DBAsOngt.js → KnowledgeGraph-DzjnD571.js} +1 -1
- package/src/assets/web-panel/assets/{Logs-COt8g9LO.js → Logs-BhWzVDtN.js} +2 -2
- package/src/assets/web-panel/assets/{MarkdownRenderer-3Lfht1Fd.js → MarkdownRenderer-BDape9cx.js} +1 -1
- package/src/assets/web-panel/assets/{Marketplace-C9Zk9Pb2.js → Marketplace-DJtL5GV7.js} +1 -1
- package/src/assets/web-panel/assets/{McpTools-DDWDFWPO.js → McpTools-CZcQygI7.js} +5 -5
- package/src/assets/web-panel/assets/{Memory-BRmVye_o.js → Memory-DUWSDUUu.js} +2 -2
- package/src/assets/web-panel/assets/{MobileBridge-CppBbWpD.js → MobileBridge-BTwq2WKY.js} +1 -1
- package/src/assets/web-panel/assets/MobileProjects-NZvvfDME.js +1 -0
- package/src/assets/web-panel/assets/{Mtc-CcWpeGa6.js → Mtc-BiGugEQm.js} +6 -6
- package/src/assets/web-panel/assets/{MtcAudit-BcllEq0K.js → MtcAudit-DY7a6gUe.js} +4 -4
- package/src/assets/web-panel/assets/{Multisig-BFD1CarL.js → Multisig-BQbDu_w7.js} +3 -3
- package/src/assets/web-panel/assets/{NLProgramming-QNXXYvjz.js → NLProgramming-CM2wXTLv.js} +1 -1
- package/src/assets/web-panel/assets/{Notes-07YwgQVy.js → Notes-DI6gGs1v.js} +4 -4
- package/src/assets/web-panel/assets/{NotificationSettings-bOccEInE.js → NotificationSettings-BsZOLWAB.js} +1 -1
- package/src/assets/web-panel/assets/{OrderTableRenderer-DlRi29WL.js → OrderTableRenderer-C48mu09U.js} +1 -1
- package/src/assets/web-panel/assets/{Organization-CBaezzEr.js → Organization-Cds0_tXr.js} +4 -4
- package/src/assets/web-panel/assets/{Overflow-Bl5NohFO.js → Overflow-WunYMD87.js} +1 -1
- package/src/assets/web-panel/assets/{P2P-CLMal9Aw.js → P2P-Bo6j4DDv.js} +2 -2
- package/src/assets/web-panel/assets/{PdhVaultBrowser-CGO0NITk.js → PdhVaultBrowser-Bh2XPD5f.js} +3 -3
- package/src/assets/web-panel/assets/{Permissions-CPSw48E2.js → Permissions-EMvMMC8N.js} +3 -3
- package/src/assets/web-panel/assets/{PersonalDataHub-CDM5n134.js → PersonalDataHub-AX3ypUDV.js} +2 -2
- package/src/assets/web-panel/assets/{Pipeline-BdsqG7WV.js → Pipeline-BOx8HOgL.js} +1 -1
- package/src/assets/web-panel/assets/{Privacy-B6L4E-M7.js → Privacy-tBeTVQVM.js} +1 -1
- package/src/assets/web-panel/assets/{ProjectInit-DXPyZKRs.js → ProjectInit-CVYg1W4M.js} +2 -2
- package/src/assets/web-panel/assets/{ProjectSettings-BrCC3xYT.js → ProjectSettings-Dvt5I7y1.js} +2 -2
- package/src/assets/web-panel/assets/{Projects-BYmuHC2q.js → Projects-Cc7FC9-e.js} +1 -1
- package/src/assets/web-panel/assets/{Providers-CbCe2eUl.js → Providers-Ct8xqY-E.js} +1 -1
- package/src/assets/web-panel/assets/{QrScannerModal-DkukgCrR.js → QrScannerModal-Cni8Ooja.js} +1 -1
- package/src/assets/web-panel/assets/{QuickAsk-D-htOrV8.js → QuickAsk-BJVLKdxr.js} +1 -1
- package/src/assets/web-panel/assets/{Recommend-Bm7SIfzu.js → Recommend-DPG8Rbbm.js} +1 -1
- package/src/assets/web-panel/assets/{RemoteSession-C9rP-iMc.js → RemoteSession-DsdR7dCt.js} +2 -2
- package/src/assets/web-panel/assets/{Reputation-kGlUgbsl.js → Reputation-Csg1YM-b.js} +1 -1
- package/src/assets/web-panel/assets/{Row-PBupbbUA.js → Row-D9DoFoEf.js} +1 -1
- package/src/assets/web-panel/assets/{RssFeed-njlwKUGK.js → RssFeed-BsvKrXMS.js} +2 -2
- package/src/assets/web-panel/assets/{Search-DLdEmgBw.js → Search-DFUStfgQ.js} +1 -1
- package/src/assets/web-panel/assets/{Security-SXGPSHdu.js → Security-ILg1zxdK.js} +4 -4
- package/src/assets/web-panel/assets/{Services-CuFGnNsb.js → Services-Dd41PCaY.js} +2 -2
- package/src/assets/web-panel/assets/{Skeleton-CtUC0mDa.js → Skeleton-Bzo1cN80.js} +1 -1
- package/src/assets/web-panel/assets/{Skills-P1qgOVFn.js → Skills-BDHh17P0.js} +1 -1
- package/src/assets/web-panel/assets/{Sla-CsppE8YV.js → Sla-jl7UTiRY.js} +1 -1
- package/src/assets/web-panel/assets/{SpeechSettings-Cq1oG56m.js → SpeechSettings-BePa-RF6.js} +1 -1
- package/src/assets/web-panel/assets/{SyncSettings-DdKW6V-C.js → SyncSettings-DiqqDKGZ.js} +2 -2
- package/src/assets/web-panel/assets/{Tasks-BCc9san5.js → Tasks-DYQ_6lxj.js} +1 -1
- package/src/assets/web-panel/assets/{Templates-CcHC1dMA.js → Templates-JiDeI06L.js} +1 -1
- package/src/assets/web-panel/assets/{Tenant-CA4PqU22.js → Tenant-hhgI6zwS.js} +1 -1
- package/src/assets/web-panel/assets/Terminal-CjTZPa37.js +3 -0
- package/src/assets/web-panel/assets/{TimelineRenderer-DHWTSvOy.js → TimelineRenderer-B8uB5Llk.js} +1 -1
- package/src/assets/web-panel/assets/{Tokens-Ddz-P_yh.js → Tokens-C3b_6r2z.js} +1 -1
- package/src/assets/web-panel/assets/{Trigger-ePmrmLZ1.js → Trigger-tIP7HKVe.js} +1 -1
- package/src/assets/web-panel/assets/{Trust-DmuK1CCE.js → Trust-vjoc11vJ.js} +1 -1
- package/src/assets/web-panel/assets/{UkeySign-CqqBuRrT.js → UkeySign-DUPBnGuH.js} +1 -1
- package/src/assets/web-panel/assets/{VideoEditing-BGlR3uWM.js → VideoEditing-CBvhUY5T.js} +1 -1
- package/src/assets/web-panel/assets/{Wallet-Ba_Qx6Ga.js → Wallet-DUaQ0uA5.js} +4 -4
- package/src/assets/web-panel/assets/{WebAuthn-29neXKt2.js → WebAuthn-DllSivjE.js} +5 -5
- package/src/assets/web-panel/assets/{WorkflowEditor-lD0lR0UQ.js → WorkflowEditor-xc8D3uO6.js} +1 -1
- package/src/assets/web-panel/assets/{chat-B3stH01u.js → chat-BVicU7oA.js} +1 -1
- package/src/assets/web-panel/assets/{colors-Czly56MN.js → colors-DJzLmhLm.js} +1 -1
- package/src/assets/web-panel/assets/{compact-item-C0TkbkAJ.js → compact-item-BvYRfThE.js} +1 -1
- package/src/assets/web-panel/assets/{createContext-BJmpSlf1.js → createContext-DGXxCBKR.js} +1 -1
- package/src/assets/web-panel/assets/devWarning-BT46Y_QI.js +1 -0
- package/src/assets/web-panel/assets/{hasIn-D1q-zw7u.js → hasIn-SBDJB1y4.js} +1 -1
- package/src/assets/web-panel/assets/{index-DV-twjNO.js → index-1SdDS6lR.js} +1 -1
- package/src/assets/web-panel/assets/{index-B-kTvMq7.js → index-9gdI7uUW.js} +1 -1
- package/src/assets/web-panel/assets/index-B7nuIywP.js +1 -0
- package/src/assets/web-panel/assets/{index-Bm_-RKal.js → index-BJZf9DhV.js} +1 -1
- package/src/assets/web-panel/assets/{index-CQftlZn-.js → index-BO1itZaY.js} +3 -3
- package/src/assets/web-panel/assets/{index-Dw3L_smW.js → index-BTrBGBJs.js} +1 -1
- package/src/assets/web-panel/assets/{index-C7sFKQ53.js → index-BYqz4Wrr.js} +1 -1
- package/src/assets/web-panel/assets/{index-DlvsSuBC.js → index-Bf-ctERC.js} +1 -1
- package/src/assets/web-panel/assets/{index-ChESIe5r.js → index-CKyEivf8.js} +1 -1
- package/src/assets/web-panel/assets/{index-DaqEHaCg.js → index-CdGY9P8a.js} +1 -1
- package/src/assets/web-panel/assets/{index-DZeoKL6t.js → index-Cf-O_g_8.js} +1 -1
- package/src/assets/web-panel/assets/{index-XWV8JWMc.js → index-CloCgBUR.js} +1 -1
- package/src/assets/web-panel/assets/{index-CpMIdPJ0.js → index-CoJAO1Lm.js} +1 -1
- package/src/assets/web-panel/assets/{index-CgS8XUBU.js → index-Crk5NTb0.js} +1 -1
- package/src/assets/web-panel/assets/{index-DmUta2lw.js → index-Cs2rmLFt.js} +1 -1
- package/src/assets/web-panel/assets/{index-Dpz_7sf-.js → index-CtwqO5qo.js} +1 -1
- package/src/assets/web-panel/assets/{index-DRcQsPez.js → index-D7HimK0M.js} +1 -1
- package/src/assets/web-panel/assets/{index-BEt2-3x5.js → index-DCdRAgjy.js} +1 -1
- package/src/assets/web-panel/assets/{index-CHvPlzWJ.js → index-DCqRS6da.js} +1 -1
- package/src/assets/web-panel/assets/{index-BDHXx65P.js → index-DHVLJ8q0.js} +1 -1
- package/src/assets/web-panel/assets/{index-K4YaPDOn.js → index-DKrvvWm4.js} +1 -1
- package/src/assets/web-panel/assets/{index-C1pOqU7D.js → index-DMarmfrv.js} +1 -1
- package/src/assets/web-panel/assets/{index-DAhrn9EJ.js → index-DRfiXB_p.js} +1 -1
- package/src/assets/web-panel/assets/{index-cPjWjvmW.js → index-Dm_77jgR.js} +1 -1
- package/src/assets/web-panel/assets/{index-C27TnOoT.js → index-DnjPdvR6.js} +1 -1
- package/src/assets/web-panel/assets/{index-upY9rkGZ.js → index-DonCnfMr.js} +1 -1
- package/src/assets/web-panel/assets/{index-CbvNJUwu.js → index-DvUTDYXX.js} +1 -1
- package/src/assets/web-panel/assets/{index-BVwJ3jht.js → index-FxZzRyE1.js} +1 -1
- package/src/assets/web-panel/assets/{index-DIF6XHIi.js → index-MjC4G09d.js} +1 -1
- package/src/assets/web-panel/assets/{index-B8KqsomG.js → index-VMfwIH5G.js} +1 -1
- package/src/assets/web-panel/assets/{index-8GKykbS4.js → index-YJkzy8ZF.js} +1 -1
- package/src/assets/web-panel/assets/{index-DpGs96FC.js → index-d5BVd-cL.js} +1 -1
- package/src/assets/web-panel/assets/{index-DO8xOwxi.js → index-e4fXH_WO.js} +1 -1
- package/src/assets/web-panel/assets/{index-Cu_4HaUS.js → index-hxTODbM1.js} +1 -1
- package/src/assets/web-panel/assets/{index-Cf7Gr3NG.js → index-lpaGoTH3.js} +1 -1
- package/src/assets/web-panel/assets/{index-DBqAm8es.js → index-wcCL-miV.js} +1 -1
- package/src/assets/web-panel/assets/{index-ZDUhX41Z.js → index-xfluRkCT.js} +1 -1
- package/src/assets/web-panel/assets/index-ycwAfC-i.js +1 -0
- package/src/assets/web-panel/assets/{index-z8ATo2aQ.js → index-z1ZylsLq.js} +1 -1
- package/src/assets/web-panel/assets/{initDefaultProps-QWEXi6-t.js → initDefaultProps-C1zO1KnY.js} +1 -1
- package/src/assets/web-panel/assets/{motion-BfrQwvV1.js → motion-CWLtEGmC.js} +1 -1
- package/src/assets/web-panel/assets/{move-Dn7x0H9t.js → move-DzD_C3Vm.js} +1 -1
- package/src/assets/web-panel/assets/{mtc-parser-F61D_TgE.js → mtc-parser-D880PYJl.js} +1 -1
- package/src/assets/web-panel/assets/{omit-4SQZ39PF.js → omit-CFR_z2ov.js} +1 -1
- package/src/assets/web-panel/assets/{pickAttrs-C7DTe126.js → pickAttrs-srIhgrSC.js} +1 -1
- package/src/assets/web-panel/assets/{placementArrow-40NeyzDK.js → placementArrow-C1rXpibP.js} +1 -1
- package/src/assets/web-panel/assets/{responsiveObserve-BiUJ6qWs.js → responsiveObserve-C30gRcSS.js} +1 -1
- package/src/assets/web-panel/assets/{slide-CacJTM3f.js → slide-CvtKiMfV.js} +1 -1
- package/src/assets/web-panel/assets/{statusUtils-K812HB-k.js → statusUtils-DNHO7ITG.js} +1 -1
- package/src/assets/web-panel/assets/{styleChecker-CKeHA2eM.js → styleChecker-Dt_Sl-g4.js} +1 -1
- package/src/assets/web-panel/assets/{useFlexGapSupport-CKl4FqXF.js → useFlexGapSupport-Cjev42Gh.js} +1 -1
- package/src/assets/web-panel/assets/{useFs-DPoiJjSZ.js → useFs-Baxglj2V.js} +1 -1
- package/src/assets/web-panel/assets/{usePersonalDataHub-aHwBspmt.js → usePersonalDataHub-B4NkcLaP.js} +1 -1
- package/src/assets/web-panel/assets/{vnode-DrA-GuCT.js → vnode-mIRkdT7o.js} +1 -1
- package/src/assets/web-panel/assets/{zoom-G3LMswgy.js → zoom-HzI5DlH_.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 +64 -2
- package/src/commands/agent.js +45 -3
- package/src/commands/background-session.js +103 -0
- package/src/commands/config.js +70 -5
- package/src/commands/context.js +36 -2
- package/src/commands/hub.js +19 -1
- 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/team.js +20 -7
- package/src/commands/update.js +65 -10
- package/src/esm-bridge.js +3 -0
- 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 +92 -4
- package/src/harness/jsonl-session-store.js +13 -6
- package/src/harness/mcp-client.js +200 -1
- package/src/index.js +123 -55
- package/src/lazy-dispatch.js +86 -85
- 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-ipc-bus.js +32 -2
- package/src/lib/agent-sandbox.js +16 -2
- package/src/lib/agent-schedule-store.js +55 -0
- package/src/lib/agent-tool-admission.js +104 -0
- package/src/lib/api-key-helper.js +10 -2
- package/src/lib/async-hook-supervisor.cjs +26 -4
- package/src/lib/background-interaction-resolver.js +71 -0
- package/src/lib/background-session-transport.js +59 -1
- package/src/lib/changelog.js +19 -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/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/crash-reporter.js +6 -0
- package/src/lib/diff-review-follow-up.js +154 -0
- package/src/lib/downloader.js +17 -3
- package/src/lib/ensure-utf8.js +11 -3
- 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 +34 -15
- package/src/lib/hook-runner.cjs +48 -3
- 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 +111 -21
- package/src/lib/ide-context.js +128 -1
- package/src/lib/interaction-adapter.js +39 -0
- package/src/lib/ipc-attach-protocol.js +98 -0
- package/src/lib/json-schema-validate.js +190 -36
- package/src/lib/lan-pairing-preflight.js +13 -2
- package/src/lib/lsp/__tests__/lsp-server-registry.test.js +18 -0
- package/src/lib/lsp/lsp-client.js +18 -1
- package/src/lib/lsp/lsp-manager.js +4 -0
- package/src/lib/lsp/lsp-server-registry.js +11 -0
- package/src/lib/mcp-oauth.js +8 -2
- package/src/lib/npm-invocation.js +29 -0
- package/src/lib/packer/pkg-runner.js +13 -2
- package/src/lib/packer/precheck.js +33 -16
- package/src/lib/packer/web-panel-builder.js +16 -2
- 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 +39 -4
- 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 +2 -1
- 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 +295 -0
- package/src/lib/process-execution-broker/index.js +440 -14
- package/src/lib/process-execution-broker/patch-child-process.js +2 -3
- package/src/lib/process-execution-broker/platform-sandbox.js +351 -0
- package/src/lib/process-manager.js +18 -3
- package/src/lib/provenance-api.js +6 -0
- package/src/lib/proxy-bootstrap.js +19 -0
- package/src/lib/publish-workspace.js +14 -6
- package/src/lib/repl-rewind.js +9 -4
- package/src/lib/secret-store.js +159 -0
- package/src/lib/service-manager.js +64 -29
- package/src/lib/session-hooks.js +328 -473
- package/src/lib/session-telemetry-middleware.js +6 -0
- package/src/lib/settings-hook-events.cjs +61 -1
- package/src/lib/settings-hooks.cjs +4 -0
- package/src/lib/side-effect-ledger.js +22 -0
- package/src/lib/sub-agent-context.js +5 -0
- package/src/lib/sub-agent-profiles.js +4 -0
- package/src/lib/trace-api.js +6 -0
- package/src/lib/turn-context.js +14 -8
- package/src/lib/unattended-action-policy.js +83 -0
- package/src/lib/update-notice.js +9 -45
- package/src/lib/with-file-lock.js +7 -0
- package/src/lib/ws-server.js +1 -0
- package/src/repl/agent-repl.js +71 -13
- package/src/repl/clipboard-copy.js +8 -2
- package/src/repl/pr-comments.js +30 -7
- package/src/runtime/agent-core.js +439 -87
- 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 +63 -0
- package/src/runtime/headless-stream.js +269 -17
- package/src/runtime/mcp-config.js +16 -1
- package/src/workers/background-agent-worker.js +80 -1
- package/src/assets/web-panel/assets/AppLayout-7iSKDmlI.js +0 -9
- package/src/assets/web-panel/assets/ChatBubbleRenderer-DcbUHeGW.js +0 -1
- package/src/assets/web-panel/assets/Dashboard-BAKYNavw.js +0 -3
- package/src/assets/web-panel/assets/MobileProjects-C4GKn1dH.js +0 -1
- package/src/assets/web-panel/assets/Terminal-Fj7ea0In.js +0 -3
- package/src/assets/web-panel/assets/devWarning-CDI8Sbaj.js +0 -1
- package/src/assets/web-panel/assets/index-96LBR96V.js +0 -1
- package/src/assets/web-panel/assets/index-DyQkiW24.js +0 -1
|
@@ -0,0 +1,354 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Durable Event Runtime inbox/outbox.
|
|
3
|
+
*
|
|
4
|
+
* The in-memory EventEmitter is intentionally kept as the fast path. This
|
|
5
|
+
* store is the crash-safe boundary for producers that need delivery across a
|
|
6
|
+
* process restart: every record has a deterministic event id, a bounded
|
|
7
|
+
* attempt count, and a renewable-style execution lease. State is rewritten
|
|
8
|
+
* atomically under a directory lock so two daemon processes cannot claim the
|
|
9
|
+
* same event.
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
import fs from "node:fs";
|
|
13
|
+
import os from "node:os";
|
|
14
|
+
import path from "node:path";
|
|
15
|
+
import { createHash, randomUUID } from "node:crypto";
|
|
16
|
+
import { withFileLock } from "./with-file-lock.js";
|
|
17
|
+
|
|
18
|
+
const QUEUES = new Set(["inbox", "outbox"]);
|
|
19
|
+
|
|
20
|
+
function defaultDir() {
|
|
21
|
+
return path.join(os.homedir(), ".chainlesschain", "event-runtime");
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
function eventId(event, explicitId = null) {
|
|
25
|
+
if (explicitId != null && String(explicitId)) return String(explicitId);
|
|
26
|
+
const supplied = event?.event_id || event?.eventId || event?.id;
|
|
27
|
+
if (supplied != null && String(supplied)) return String(supplied);
|
|
28
|
+
const digest = createHash("sha256")
|
|
29
|
+
.update(JSON.stringify(event || {}))
|
|
30
|
+
.digest("hex")
|
|
31
|
+
.slice(0, 32);
|
|
32
|
+
return `evt_${digest}`;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function clone(value) {
|
|
36
|
+
return value == null ? value : JSON.parse(JSON.stringify(value));
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export class EventRuntimeStore {
|
|
40
|
+
constructor({
|
|
41
|
+
dir = defaultDir(),
|
|
42
|
+
now = () => Date.now(),
|
|
43
|
+
owner = `${process.pid}:${randomUUID()}`,
|
|
44
|
+
maxAttempts = 5,
|
|
45
|
+
leaseMs = 120000,
|
|
46
|
+
lockTimeoutMs = 2000,
|
|
47
|
+
maxQueueLength = 10000,
|
|
48
|
+
} = {}) {
|
|
49
|
+
this.dir = dir;
|
|
50
|
+
this._now = typeof now === "function" ? now : () => Number(now);
|
|
51
|
+
this.owner = String(owner);
|
|
52
|
+
this.maxAttempts = Math.max(1, Number(maxAttempts) || 5);
|
|
53
|
+
this.leaseMs = Math.max(1, Number(leaseMs) || 120000);
|
|
54
|
+
this.lockTimeoutMs = Math.max(0, Number(lockTimeoutMs) || 2000);
|
|
55
|
+
this.maxQueueLength = Math.max(1, Number(maxQueueLength) || 10000);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
_assertQueue(queue) {
|
|
59
|
+
if (!QUEUES.has(queue)) throw new Error(`Unknown event queue: ${queue}`);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
_file(queue) {
|
|
63
|
+
this._assertQueue(queue);
|
|
64
|
+
return path.join(this.dir, `${queue}.json`);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
_ensureDir() {
|
|
68
|
+
fs.mkdirSync(this.dir, { recursive: true, mode: 0o700 });
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
_read(queue) {
|
|
72
|
+
const file = this._file(queue);
|
|
73
|
+
try {
|
|
74
|
+
const value = JSON.parse(fs.readFileSync(file, "utf8"));
|
|
75
|
+
return Array.isArray(value) ? value : [];
|
|
76
|
+
} catch {
|
|
77
|
+
return [];
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
_write(queue, records) {
|
|
82
|
+
this._ensureDir();
|
|
83
|
+
const file = this._file(queue);
|
|
84
|
+
const tmp = `${file}.${process.pid}.${randomUUID()}.tmp`;
|
|
85
|
+
fs.writeFileSync(tmp, JSON.stringify(records, null, 2), "utf8");
|
|
86
|
+
fs.renameSync(tmp, file);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
_mutate(queue, fn) {
|
|
90
|
+
this._ensureDir();
|
|
91
|
+
return withFileLock(
|
|
92
|
+
path.join(this.dir, ".event-runtime"),
|
|
93
|
+
() => {
|
|
94
|
+
const records = this._read(queue);
|
|
95
|
+
const result = fn(records);
|
|
96
|
+
this._write(queue, records);
|
|
97
|
+
return result;
|
|
98
|
+
},
|
|
99
|
+
{ timeoutMs: this.lockTimeoutMs, failIfUnavailable: true },
|
|
100
|
+
);
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
enqueue(queue, event, { id = null, metadata = null } = {}) {
|
|
104
|
+
const key = eventId(event, id);
|
|
105
|
+
return this._mutate(queue, (records) => {
|
|
106
|
+
const existing = records.find((record) => record.id === key);
|
|
107
|
+
if (existing) return { ...clone(existing), duplicate: true };
|
|
108
|
+
const active = records.reduce(
|
|
109
|
+
(count, record) =>
|
|
110
|
+
count +
|
|
111
|
+
(record.status === "done" || record.status === "dead" ? 0 : 1),
|
|
112
|
+
0,
|
|
113
|
+
);
|
|
114
|
+
if (active >= this.maxQueueLength) {
|
|
115
|
+
const error = new Error(
|
|
116
|
+
`Event Runtime ${queue} queue is full (${this.maxQueueLength})`,
|
|
117
|
+
);
|
|
118
|
+
error.code = "CC_EVENT_RUNTIME_BACKPRESSURE";
|
|
119
|
+
throw error;
|
|
120
|
+
}
|
|
121
|
+
const record = {
|
|
122
|
+
id: key,
|
|
123
|
+
event: clone(event || {}),
|
|
124
|
+
metadata: clone(metadata),
|
|
125
|
+
status: "pending",
|
|
126
|
+
attempts: 0,
|
|
127
|
+
createdAt: this._now(),
|
|
128
|
+
nextAttemptAt: this._now(),
|
|
129
|
+
lease: null,
|
|
130
|
+
};
|
|
131
|
+
records.push(record);
|
|
132
|
+
return clone(record);
|
|
133
|
+
});
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
enqueueInbox(event, options) {
|
|
137
|
+
return this.enqueue("inbox", event, options);
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
enqueueOutbox(event, options) {
|
|
141
|
+
return this.enqueue("outbox", event, options);
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
/** Requeue abandoned claims and return claimable records for this owner. */
|
|
145
|
+
claim(queue, { limit = 50, now = this._now(), leaseMs = this.leaseMs } = {}) {
|
|
146
|
+
const t = Number(now);
|
|
147
|
+
const ttl = Math.max(1, Number(leaseMs) || this.leaseMs);
|
|
148
|
+
const max = Math.max(1, Number(limit) || 50);
|
|
149
|
+
return this._mutate(queue, (records) => {
|
|
150
|
+
const claimed = [];
|
|
151
|
+
for (const record of records) {
|
|
152
|
+
if (
|
|
153
|
+
record.status === "processing" &&
|
|
154
|
+
Number(record.lease?.expiresAt) <= t
|
|
155
|
+
) {
|
|
156
|
+
record.status = "pending";
|
|
157
|
+
record.lease = null;
|
|
158
|
+
}
|
|
159
|
+
if (claimed.length >= max || record.status !== "pending") continue;
|
|
160
|
+
if (Number(record.nextAttemptAt || 0) > t) continue;
|
|
161
|
+
record.status = "processing";
|
|
162
|
+
record.attempts = Number(record.attempts || 0) + 1;
|
|
163
|
+
record.lease = { owner: this.owner, claimedAt: t, expiresAt: t + ttl };
|
|
164
|
+
claimed.push(clone(record));
|
|
165
|
+
}
|
|
166
|
+
return claimed;
|
|
167
|
+
});
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
claimInbox(options) {
|
|
171
|
+
return this.claim("inbox", options);
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
claimOutbox(options) {
|
|
175
|
+
return this.claim("outbox", options);
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
acknowledge(queue, id, result = null, { owner = null } = {}) {
|
|
179
|
+
return this._mutate(queue, (records) => {
|
|
180
|
+
const record = records.find((item) => item.id === String(id));
|
|
181
|
+
if (!record) return null;
|
|
182
|
+
const expectedOwner = owner ? String(owner) : null;
|
|
183
|
+
if (
|
|
184
|
+
expectedOwner &&
|
|
185
|
+
(record.status !== "processing" ||
|
|
186
|
+
record.lease?.owner !== expectedOwner)
|
|
187
|
+
) {
|
|
188
|
+
return null;
|
|
189
|
+
}
|
|
190
|
+
record.status = "done";
|
|
191
|
+
record.result = clone(result);
|
|
192
|
+
record.completedAt = this._now();
|
|
193
|
+
record.lease = null;
|
|
194
|
+
return clone(record);
|
|
195
|
+
});
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
acknowledgeInbox(id, result, options) {
|
|
199
|
+
return this.acknowledge("inbox", id, result, options);
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
acknowledgeOutbox(id, result, options) {
|
|
203
|
+
return this.acknowledge("outbox", id, result, options);
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
fail(
|
|
207
|
+
queue,
|
|
208
|
+
id,
|
|
209
|
+
error,
|
|
210
|
+
{ retryDelayMs = 1000, maxAttempts = this.maxAttempts, owner = null } = {},
|
|
211
|
+
) {
|
|
212
|
+
return this._mutate(queue, (records) => {
|
|
213
|
+
const record = records.find((item) => item.id === String(id));
|
|
214
|
+
if (!record) return null;
|
|
215
|
+
const expectedOwner = owner ? String(owner) : null;
|
|
216
|
+
if (
|
|
217
|
+
expectedOwner &&
|
|
218
|
+
(record.status !== "processing" ||
|
|
219
|
+
record.lease?.owner !== expectedOwner)
|
|
220
|
+
) {
|
|
221
|
+
return null;
|
|
222
|
+
}
|
|
223
|
+
record.error = String(error || "event delivery failed").slice(0, 1000);
|
|
224
|
+
record.lease = null;
|
|
225
|
+
if (
|
|
226
|
+
Number(record.attempts || 0) >=
|
|
227
|
+
Math.max(1, Number(maxAttempts) || this.maxAttempts)
|
|
228
|
+
) {
|
|
229
|
+
record.status = "dead";
|
|
230
|
+
record.deadAt = this._now();
|
|
231
|
+
} else {
|
|
232
|
+
record.status = "pending";
|
|
233
|
+
record.nextAttemptAt =
|
|
234
|
+
this._now() + Math.max(0, Number(retryDelayMs) || 0);
|
|
235
|
+
}
|
|
236
|
+
return clone(record);
|
|
237
|
+
});
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
list(queue, { status = null } = {}) {
|
|
241
|
+
return this._read(queue)
|
|
242
|
+
.filter((record) => !status || record.status === status)
|
|
243
|
+
.map(clone);
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
listInbox(options) {
|
|
247
|
+
return this.list("inbox", options);
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
listOutbox(options) {
|
|
251
|
+
return this.list("outbox", options);
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
/**
|
|
255
|
+
* Return a lock-consistent, cross-process health snapshot without claiming
|
|
256
|
+
* work. Queue capacity is measured from active records because completed and
|
|
257
|
+
* dead-letter records do not participate in producer backpressure.
|
|
258
|
+
*/
|
|
259
|
+
getHealthSnapshot({ now = this._now(), highWatermarkRatio = 0.8 } = {}) {
|
|
260
|
+
const t = Number(now);
|
|
261
|
+
const highWatermark = Math.min(
|
|
262
|
+
1,
|
|
263
|
+
Math.max(0, Number(highWatermarkRatio) || 0.8),
|
|
264
|
+
);
|
|
265
|
+
this._ensureDir();
|
|
266
|
+
return withFileLock(
|
|
267
|
+
path.join(this.dir, ".event-runtime"),
|
|
268
|
+
() => {
|
|
269
|
+
const queues = {};
|
|
270
|
+
for (const queue of QUEUES) {
|
|
271
|
+
const records = this._read(queue);
|
|
272
|
+
const summary = {
|
|
273
|
+
total: records.length,
|
|
274
|
+
active: 0,
|
|
275
|
+
pending: 0,
|
|
276
|
+
processing: 0,
|
|
277
|
+
done: 0,
|
|
278
|
+
dead: 0,
|
|
279
|
+
unknown: 0,
|
|
280
|
+
claimable: 0,
|
|
281
|
+
delayed: 0,
|
|
282
|
+
expiredLeases: 0,
|
|
283
|
+
oldestActiveAgeMs: null,
|
|
284
|
+
availableCapacity: this.maxQueueLength,
|
|
285
|
+
utilization: 0,
|
|
286
|
+
pressure: "normal",
|
|
287
|
+
};
|
|
288
|
+
let oldestActiveAt = null;
|
|
289
|
+
for (const record of records) {
|
|
290
|
+
const status = String(record.status || "unknown");
|
|
291
|
+
if (["pending", "processing", "done", "dead"].includes(status)) {
|
|
292
|
+
summary[status] += 1;
|
|
293
|
+
} else summary.unknown += 1;
|
|
294
|
+
const active = status !== "done" && status !== "dead";
|
|
295
|
+
if (active) {
|
|
296
|
+
summary.active += 1;
|
|
297
|
+
const createdAt = Number(record.createdAt);
|
|
298
|
+
if (
|
|
299
|
+
Number.isFinite(createdAt) &&
|
|
300
|
+
(oldestActiveAt == null || createdAt < oldestActiveAt)
|
|
301
|
+
) {
|
|
302
|
+
oldestActiveAt = createdAt;
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
if (status === "pending") {
|
|
306
|
+
if (Number(record.nextAttemptAt || 0) <= t)
|
|
307
|
+
summary.claimable += 1;
|
|
308
|
+
else summary.delayed += 1;
|
|
309
|
+
} else if (
|
|
310
|
+
status === "processing" &&
|
|
311
|
+
Number(record.lease?.expiresAt) <= t
|
|
312
|
+
) {
|
|
313
|
+
summary.expiredLeases += 1;
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
summary.availableCapacity = Math.max(
|
|
317
|
+
0,
|
|
318
|
+
this.maxQueueLength - summary.active,
|
|
319
|
+
);
|
|
320
|
+
summary.utilization = Number(
|
|
321
|
+
(summary.active / this.maxQueueLength).toFixed(4),
|
|
322
|
+
);
|
|
323
|
+
summary.pressure =
|
|
324
|
+
summary.active >= this.maxQueueLength
|
|
325
|
+
? "full"
|
|
326
|
+
: summary.utilization >= highWatermark
|
|
327
|
+
? "high"
|
|
328
|
+
: "normal";
|
|
329
|
+
summary.oldestActiveAgeMs =
|
|
330
|
+
oldestActiveAt == null ? null : Math.max(0, t - oldestActiveAt);
|
|
331
|
+
queues[queue] = summary;
|
|
332
|
+
}
|
|
333
|
+
const pressure = Object.values(queues).some(
|
|
334
|
+
(queue) => queue.pressure === "full",
|
|
335
|
+
)
|
|
336
|
+
? "full"
|
|
337
|
+
: Object.values(queues).some((queue) => queue.pressure === "high")
|
|
338
|
+
? "high"
|
|
339
|
+
: "normal";
|
|
340
|
+
return {
|
|
341
|
+
schema: "chainlesschain.event-runtime-health.v1",
|
|
342
|
+
observedAt: new Date(t).toISOString(),
|
|
343
|
+
queueLimit: this.maxQueueLength,
|
|
344
|
+
highWatermarkRatio: highWatermark,
|
|
345
|
+
pressure,
|
|
346
|
+
queues,
|
|
347
|
+
};
|
|
348
|
+
},
|
|
349
|
+
{ timeoutMs: this.lockTimeoutMs, failIfUnavailable: true },
|
|
350
|
+
);
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
export { eventId };
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Recoverable consumer for EventRuntimeStore.
|
|
3
|
+
*
|
|
4
|
+
* Handlers are injected because inbox events and outbox deliveries have
|
|
5
|
+
* different side effects in different hosts (Hooks, Agenda, WebSocket, CI).
|
|
6
|
+
* The worker owns only claim/ack/retry semantics and never invents an
|
|
7
|
+
* authority policy for the event payload.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
export class EventRuntimeWorker {
|
|
11
|
+
constructor({
|
|
12
|
+
store,
|
|
13
|
+
onInbox = async () => {},
|
|
14
|
+
onOutbox = async () => {},
|
|
15
|
+
now = () => Date.now(),
|
|
16
|
+
sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms)),
|
|
17
|
+
batchSize = 50,
|
|
18
|
+
retryDelayMs = 1000,
|
|
19
|
+
} = {}) {
|
|
20
|
+
if (!store) throw new Error("EventRuntimeWorker requires a store");
|
|
21
|
+
this.store = store;
|
|
22
|
+
this.onInbox = onInbox;
|
|
23
|
+
this.onOutbox = onOutbox;
|
|
24
|
+
this.now = now;
|
|
25
|
+
this.sleep = sleep;
|
|
26
|
+
this.batchSize = Math.max(1, Number(batchSize) || 50);
|
|
27
|
+
this.retryDelayMs = Math.max(0, Number(retryDelayMs) || 0);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
async _drain(queue, handler, stats) {
|
|
31
|
+
const claimed =
|
|
32
|
+
queue === "inbox"
|
|
33
|
+
? this.store.claimInbox({ limit: this.batchSize, now: this.now() })
|
|
34
|
+
: this.store.claimOutbox({ limit: this.batchSize, now: this.now() });
|
|
35
|
+
for (const record of claimed) {
|
|
36
|
+
try {
|
|
37
|
+
const result = await handler(record.event, record);
|
|
38
|
+
const options = { owner: record.lease?.owner };
|
|
39
|
+
const settled =
|
|
40
|
+
queue === "inbox"
|
|
41
|
+
? this.store.acknowledgeInbox(record.id, result, options)
|
|
42
|
+
: this.store.acknowledgeOutbox(record.id, result, options);
|
|
43
|
+
if (settled === null && options.owner) stats[`${queue}LeaseLost`] += 1;
|
|
44
|
+
else stats[`${queue}Acked`] += 1;
|
|
45
|
+
} catch (error) {
|
|
46
|
+
const options = {
|
|
47
|
+
retryDelayMs: this.retryDelayMs,
|
|
48
|
+
owner: record.lease?.owner,
|
|
49
|
+
};
|
|
50
|
+
const settled = this.store.fail(
|
|
51
|
+
queue,
|
|
52
|
+
record.id,
|
|
53
|
+
error?.message || error,
|
|
54
|
+
options,
|
|
55
|
+
);
|
|
56
|
+
if (settled === null && options.owner) stats[`${queue}LeaseLost`] += 1;
|
|
57
|
+
else stats[`${queue}Failed`] += 1;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
stats[`${queue}Claimed`] += claimed.length;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
async runOnce() {
|
|
64
|
+
const stats = {
|
|
65
|
+
inboxClaimed: 0,
|
|
66
|
+
inboxAcked: 0,
|
|
67
|
+
inboxFailed: 0,
|
|
68
|
+
inboxLeaseLost: 0,
|
|
69
|
+
outboxClaimed: 0,
|
|
70
|
+
outboxAcked: 0,
|
|
71
|
+
outboxFailed: 0,
|
|
72
|
+
outboxLeaseLost: 0,
|
|
73
|
+
};
|
|
74
|
+
await this._drain("inbox", this.onInbox, stats);
|
|
75
|
+
await this._drain("outbox", this.onOutbox, stats);
|
|
76
|
+
return stats;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
async run({ intervalMs = 1000, signal = null, maxTicks = null } = {}) {
|
|
80
|
+
let stopped = Boolean(signal?.aborted);
|
|
81
|
+
let ticks = 0;
|
|
82
|
+
const stop = () => {
|
|
83
|
+
stopped = true;
|
|
84
|
+
};
|
|
85
|
+
signal?.addEventListener?.("abort", stop, { once: true });
|
|
86
|
+
const totals = {
|
|
87
|
+
inboxClaimed: 0,
|
|
88
|
+
inboxAcked: 0,
|
|
89
|
+
inboxFailed: 0,
|
|
90
|
+
inboxLeaseLost: 0,
|
|
91
|
+
outboxClaimed: 0,
|
|
92
|
+
outboxAcked: 0,
|
|
93
|
+
outboxFailed: 0,
|
|
94
|
+
outboxLeaseLost: 0,
|
|
95
|
+
};
|
|
96
|
+
try {
|
|
97
|
+
while (!stopped) {
|
|
98
|
+
const stats = await this.runOnce();
|
|
99
|
+
for (const key of Object.keys(totals)) totals[key] += stats[key];
|
|
100
|
+
ticks += 1;
|
|
101
|
+
if (Number.isFinite(maxTicks) && ticks >= maxTicks) break;
|
|
102
|
+
await this.sleep(Math.max(0, Number(intervalMs) || 0));
|
|
103
|
+
}
|
|
104
|
+
} finally {
|
|
105
|
+
signal?.removeEventListener?.("abort", stop);
|
|
106
|
+
}
|
|
107
|
+
return { ticks, ...totals };
|
|
108
|
+
}
|
|
109
|
+
}
|
|
@@ -10,14 +10,34 @@
|
|
|
10
10
|
* @module execution-backend
|
|
11
11
|
*/
|
|
12
12
|
|
|
13
|
-
import
|
|
13
|
+
import executionBroker from "./process-execution-broker/index.js";
|
|
14
14
|
|
|
15
15
|
// ─── Exported for test injection ────────────────────────────────────
|
|
16
16
|
|
|
17
17
|
export const _deps = {
|
|
18
|
-
execSync,
|
|
18
|
+
execSync: (...args) => executionBroker.execSync(...args),
|
|
19
|
+
spawnSync: (...args) => executionBroker.spawnSync(...args),
|
|
19
20
|
};
|
|
20
21
|
|
|
22
|
+
function execArgv(command, args, options) {
|
|
23
|
+
const result = _deps.spawnSync(command, args, options);
|
|
24
|
+
if (result?.error) {
|
|
25
|
+
result.error.status ??= result.status ?? 1;
|
|
26
|
+
result.error.stdout ??= result.stdout;
|
|
27
|
+
result.error.stderr ??= result.stderr;
|
|
28
|
+
throw result.error;
|
|
29
|
+
}
|
|
30
|
+
if (!result || result.status !== 0) {
|
|
31
|
+
const status = result?.status ?? 1;
|
|
32
|
+
const error = new Error(`${command} exited with status ${status}`);
|
|
33
|
+
error.status = status;
|
|
34
|
+
error.stdout = result?.stdout;
|
|
35
|
+
error.stderr = result?.stderr;
|
|
36
|
+
throw error;
|
|
37
|
+
}
|
|
38
|
+
return result?.stdout || "";
|
|
39
|
+
}
|
|
40
|
+
|
|
21
41
|
// ─── Base class ─────────────────────────────────────────────────────
|
|
22
42
|
|
|
23
43
|
export class ExecutionBackend {
|
|
@@ -60,6 +80,9 @@ export class LocalBackend extends ExecutionBackend {
|
|
|
60
80
|
|
|
61
81
|
try {
|
|
62
82
|
const stdout = _deps.execSync(command, {
|
|
83
|
+
origin: "execution-backend:local",
|
|
84
|
+
scope: "execution-backend",
|
|
85
|
+
policy: "allow",
|
|
63
86
|
cwd,
|
|
64
87
|
encoding: "utf8",
|
|
65
88
|
timeout,
|
|
@@ -106,14 +129,32 @@ export class DockerBackend extends ExecutionBackend {
|
|
|
106
129
|
const maxBuffer = opts.maxBuffer || 1024 * 1024;
|
|
107
130
|
const cwd = opts.cwd || this.workdir;
|
|
108
131
|
|
|
109
|
-
let
|
|
132
|
+
let dockerArgs;
|
|
110
133
|
if (this.container) {
|
|
111
134
|
// Exec into existing container
|
|
112
|
-
|
|
135
|
+
dockerArgs = [
|
|
136
|
+
"exec",
|
|
137
|
+
"-w",
|
|
138
|
+
cwd,
|
|
139
|
+
this.container,
|
|
140
|
+
this.shell,
|
|
141
|
+
"-c",
|
|
142
|
+
command,
|
|
143
|
+
];
|
|
113
144
|
} else if (this.image) {
|
|
114
145
|
// Run ephemeral container
|
|
115
|
-
const volumeArgs = this.volumes.
|
|
116
|
-
|
|
146
|
+
const volumeArgs = this.volumes.flatMap((volume) => ["-v", volume]);
|
|
147
|
+
dockerArgs = [
|
|
148
|
+
"run",
|
|
149
|
+
"--rm",
|
|
150
|
+
"-w",
|
|
151
|
+
cwd,
|
|
152
|
+
...volumeArgs,
|
|
153
|
+
this.image,
|
|
154
|
+
this.shell,
|
|
155
|
+
"-c",
|
|
156
|
+
command,
|
|
157
|
+
];
|
|
117
158
|
} else {
|
|
118
159
|
return {
|
|
119
160
|
stdout: "",
|
|
@@ -123,7 +164,11 @@ export class DockerBackend extends ExecutionBackend {
|
|
|
123
164
|
}
|
|
124
165
|
|
|
125
166
|
try {
|
|
126
|
-
const stdout =
|
|
167
|
+
const stdout = execArgv("docker", dockerArgs, {
|
|
168
|
+
origin: "execution-backend:docker",
|
|
169
|
+
scope: "execution-backend",
|
|
170
|
+
policy: "allow",
|
|
171
|
+
shell: false,
|
|
127
172
|
encoding: "utf8",
|
|
128
173
|
timeout,
|
|
129
174
|
maxBuffer,
|
|
@@ -138,10 +183,6 @@ export class DockerBackend extends ExecutionBackend {
|
|
|
138
183
|
}
|
|
139
184
|
}
|
|
140
185
|
|
|
141
|
-
_escapeCommand(cmd) {
|
|
142
|
-
return cmd.replace(/"/g, '\\"');
|
|
143
|
-
}
|
|
144
|
-
|
|
145
186
|
describe() {
|
|
146
187
|
if (this.container) return `docker exec (container: ${this.container})`;
|
|
147
188
|
return `docker run (image: ${this.image})`;
|
|
@@ -183,14 +224,24 @@ export class SSHBackend extends ExecutionBackend {
|
|
|
183
224
|
}
|
|
184
225
|
|
|
185
226
|
const userHost = this.user ? `${this.user}@${this.host}` : this.host;
|
|
186
|
-
const
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
227
|
+
const sshArgs = [];
|
|
228
|
+
if (this.key) sshArgs.push("-i", this.key);
|
|
229
|
+
if (this.port !== 22) sshArgs.push("-p", String(this.port));
|
|
230
|
+
sshArgs.push(
|
|
231
|
+
"-o",
|
|
232
|
+
"StrictHostKeyChecking=no",
|
|
233
|
+
"-o",
|
|
234
|
+
"ConnectTimeout=10",
|
|
235
|
+
userHost,
|
|
236
|
+
`cd ${quotePosixShellArg(cwd)} && ${command}`,
|
|
237
|
+
);
|
|
191
238
|
|
|
192
239
|
try {
|
|
193
|
-
const stdout =
|
|
240
|
+
const stdout = execArgv("ssh", sshArgs, {
|
|
241
|
+
origin: "execution-backend:ssh",
|
|
242
|
+
scope: "execution-backend",
|
|
243
|
+
policy: "allow",
|
|
244
|
+
shell: false,
|
|
194
245
|
encoding: "utf8",
|
|
195
246
|
timeout,
|
|
196
247
|
maxBuffer,
|
|
@@ -205,16 +256,16 @@ export class SSHBackend extends ExecutionBackend {
|
|
|
205
256
|
}
|
|
206
257
|
}
|
|
207
258
|
|
|
208
|
-
_escapeCommand(cmd) {
|
|
209
|
-
return cmd.replace(/"/g, '\\"');
|
|
210
|
-
}
|
|
211
|
-
|
|
212
259
|
describe() {
|
|
213
260
|
const userHost = this.user ? `${this.user}@${this.host}` : this.host;
|
|
214
261
|
return `ssh (${userHost}:${this.port})`;
|
|
215
262
|
}
|
|
216
263
|
}
|
|
217
264
|
|
|
265
|
+
function quotePosixShellArg(value) {
|
|
266
|
+
return `'${String(value).replace(/'/g, `'"'"'`)}'`;
|
|
267
|
+
}
|
|
268
|
+
|
|
218
269
|
// ─── Factory ────────────────────────────────────────────────────────
|
|
219
270
|
|
|
220
271
|
/**
|
|
@@ -2,9 +2,14 @@
|
|
|
2
2
|
* Git integration — wraps system git commands for knowledge base versioning.
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
|
-
import { execSync, spawnSync } from "child_process";
|
|
6
5
|
import { existsSync, writeFileSync, chmodSync } from "fs";
|
|
7
6
|
import { join } from "path";
|
|
7
|
+
import { executionBroker } from "./process-execution-broker/index.js";
|
|
8
|
+
|
|
9
|
+
export const _deps = {
|
|
10
|
+
execSync: (...args) => executionBroker.execSync(...args),
|
|
11
|
+
spawnSync: (...args) => executionBroker.spawnSync(...args),
|
|
12
|
+
};
|
|
8
13
|
|
|
9
14
|
/**
|
|
10
15
|
* Check if a directory is a git repository.
|
|
@@ -80,12 +85,16 @@ export function assertSafeGitPath(p, label = "file path") {
|
|
|
80
85
|
* @returns {string} trimmed stdout
|
|
81
86
|
*/
|
|
82
87
|
export function gitExecArgs(args, cwd, opts = {}) {
|
|
83
|
-
const res = spawnSync("git", args, {
|
|
88
|
+
const res = _deps.spawnSync("git", args, {
|
|
84
89
|
cwd,
|
|
85
90
|
encoding: "utf-8",
|
|
86
91
|
windowsHide: true,
|
|
87
92
|
maxBuffer: 64 * 1024 * 1024,
|
|
88
93
|
input: opts.input,
|
|
94
|
+
origin: "git-integration:argv",
|
|
95
|
+
policy: "allow",
|
|
96
|
+
scope: "git",
|
|
97
|
+
shell: false,
|
|
89
98
|
});
|
|
90
99
|
if (res.error) throw res.error;
|
|
91
100
|
if (res.status !== 0) {
|
|
@@ -102,7 +111,7 @@ export function gitExecArgs(args, cwd, opts = {}) {
|
|
|
102
111
|
*/
|
|
103
112
|
export function gitExec(args, cwd) {
|
|
104
113
|
try {
|
|
105
|
-
return execSync(`git ${args}`, {
|
|
114
|
+
return _deps.execSync(`git ${args}`, {
|
|
106
115
|
cwd,
|
|
107
116
|
encoding: "utf-8",
|
|
108
117
|
// git diff/log/worktree-list output on a large repo easily exceeds
|
|
@@ -111,6 +120,9 @@ export function gitExec(args, cwd) {
|
|
|
111
120
|
// pass, which isn't wrapped in a degrade-gracefully try/catch).
|
|
112
121
|
maxBuffer: 64 * 1024 * 1024,
|
|
113
122
|
stdio: ["pipe", "pipe", "pipe"],
|
|
123
|
+
origin: "git-integration:shell",
|
|
124
|
+
policy: "allow",
|
|
125
|
+
scope: "git",
|
|
114
126
|
}).trim();
|
|
115
127
|
} catch (err) {
|
|
116
128
|
const stderr = err.stderr ? err.stderr.toString("utf-8").trim() : "";
|
|
@@ -138,10 +150,13 @@ export function gitStatus(dir) {
|
|
|
138
150
|
}
|
|
139
151
|
|
|
140
152
|
// Use execSync directly to preserve leading whitespace in porcelain format
|
|
141
|
-
const output = execSync("git status --porcelain", {
|
|
153
|
+
const output = _deps.execSync("git status --porcelain", {
|
|
142
154
|
cwd: dir,
|
|
143
155
|
encoding: "utf-8",
|
|
144
156
|
stdio: ["pipe", "pipe", "pipe"],
|
|
157
|
+
origin: "git-integration:shell",
|
|
158
|
+
policy: "allow",
|
|
159
|
+
scope: "git",
|
|
145
160
|
});
|
|
146
161
|
const files = output
|
|
147
162
|
.split("\n")
|