chainlesschain 0.162.170 → 0.162.172
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/bin/chainlesschain.js +8 -0
- package/package.json +6 -1
- package/src/assets/web-panel/assets/{AIOps-BTXN24I3.js → AIOps-oKCWU-LK.js} +1 -1
- package/src/assets/web-panel/assets/{ActionButton-BBB1VF4j.js → ActionButton-4CBfc5E4.js} +1 -1
- package/src/assets/web-panel/assets/{Analytics-BVCXq0k6.js → Analytics-BEmR-k_b.js} +3 -3
- package/src/assets/web-panel/assets/{AppLayout-C3EOo2s5.js → AppLayout-D6a4ZnrC.js} +5 -5
- package/src/assets/web-panel/assets/{Artifacts-CsJTZSMv.js → Artifacts-BRWIHCPl.js} +1 -1
- package/src/assets/web-panel/assets/{Audit-CP0AllJ_.js → Audit-CzGKlkTc.js} +1 -1
- package/src/assets/web-panel/assets/{BackgroundAgents-BCAjs08m.js → BackgroundAgents-CmMYN10o.js} +1 -1
- package/src/assets/web-panel/assets/{Backup-BxQI5XH4.js → Backup-CJ6e0ukD.js} +1 -1
- package/src/assets/web-panel/assets/{BaseInput-Bu-PNzC5.js → BaseInput-BpEvDrNt.js} +1 -1
- package/src/assets/web-panel/assets/{Chat-DjPQynup.js → Chat-wWuwwjKu.js} +4 -4
- package/src/assets/web-panel/assets/{ChatBubbleRenderer-CygkRopu.js → ChatBubbleRenderer-CJLyGo_u.js} +1 -1
- package/src/assets/web-panel/assets/{Checkbox-BxL6xm7Z.js → Checkbox-C6pW60Br.js} +1 -1
- package/src/assets/web-panel/assets/{Codegen-CcyB7h_6.js → Codegen-DF-nP22Y.js} +1 -1
- package/src/assets/web-panel/assets/{Col-8NQiKJJx.js → Col-BuueEwZW.js} +1 -1
- package/src/assets/web-panel/assets/{Community-B31qy5OS.js → Community-DuE0Yzwr.js} +1 -1
- package/src/assets/web-panel/assets/{Compact-C-5fAsZ4.js → Compact-BrHim8EB.js} +1 -1
- package/src/assets/web-panel/assets/{Compliance-BTSccvF0.js → Compliance-DgAet8f_.js} +1 -1
- package/src/assets/web-panel/assets/{Cowork-BXctU9IK.js → Cowork-B9wY05lx.js} +3 -3
- package/src/assets/web-panel/assets/{Cron-CxVFX9NG.js → Cron-CvNBxfJX.js} +2 -2
- package/src/assets/web-panel/assets/{Crosschain-DEF3dq8M.js → Crosschain-BnZRENa0.js} +1 -1
- package/src/assets/web-panel/assets/{DID-BmKcE9he.js → DID-DiqYLHTI.js} +2 -2
- package/src/assets/web-panel/assets/{Dashboard-CECBq_wG.js → Dashboard-t3SsRzZL.js} +2 -2
- package/src/assets/web-panel/assets/{Dropdown-U-iuPB0n.js → Dropdown-BCjMLam9.js} +1 -1
- package/src/assets/web-panel/assets/{EmailListRenderer-D8BIlSPL.js → EmailListRenderer-D2v9-IHJ.js} +1 -1
- package/src/assets/web-panel/assets/{FamilyGuardDashboard-BzQGfUH-.js → FamilyGuardDashboard-_iQu35qa.js} +1 -1
- package/src/assets/web-panel/assets/{Federation-4_WlOqLZ.js → Federation-Bkty8VqB.js} +1 -1
- package/src/assets/web-panel/assets/{FormItemContext-gtrW-DdF.js → FormItemContext-DdPW81BY.js} +1 -1
- package/src/assets/web-panel/assets/{GenericCardRenderer-BLcvJJB6.js → GenericCardRenderer-DrNYM4Zj.js} +1 -1
- package/src/assets/web-panel/assets/{Git-Bk0janGD.js → Git-BjF-B2h0.js} +2 -2
- package/src/assets/web-panel/assets/{Governance-CcfyMlV8.js → Governance-DPhGdJ1v.js} +1 -1
- package/src/assets/web-panel/assets/{Inference-CBLrAf4M.js → Inference--PPksOVj.js} +1 -1
- package/src/assets/web-panel/assets/{KnowledgeGraph-ClguhteZ.js → KnowledgeGraph-y5WdV_JV.js} +1 -1
- package/src/assets/web-panel/assets/{Logs-Dp_oeKfJ.js → Logs-B6_x7zba.js} +2 -2
- package/src/assets/web-panel/assets/MarkdownRenderer-Bg8zSyF8.js +1 -0
- package/src/assets/web-panel/assets/{Marketplace-BHk50smM.js → Marketplace-CHpfMu1A.js} +1 -1
- package/src/assets/web-panel/assets/{McpTools-BAY3CV6J.js → McpTools-qqjtX3Cf.js} +5 -5
- package/src/assets/web-panel/assets/{Memory-w0pG2AKx.js → Memory-hASSgxbw.js} +2 -2
- package/src/assets/web-panel/assets/{MobileBridge-C0VkDUE3.js → MobileBridge-Dwh7Bhcf.js} +1 -1
- package/src/assets/web-panel/assets/{MobileProjects-BCvQ80I9.js → MobileProjects-D09voh7W.js} +1 -1
- package/src/assets/web-panel/assets/{Mtc-DSIl41Tt.js → Mtc-9G1dXeId.js} +5 -5
- package/src/assets/web-panel/assets/{MtcAudit-D1mzUlIr.js → MtcAudit-CWtYXj6f.js} +2 -2
- package/src/assets/web-panel/assets/{Multisig-CeJz4VSS.js → Multisig-bDI0EnDk.js} +3 -3
- package/src/assets/web-panel/assets/{NLProgramming-_O3aweu8.js → NLProgramming-CTanRaBP.js} +1 -1
- package/src/assets/web-panel/assets/{Notes-Dt7_pO86.js → Notes-BdQENJFv.js} +3 -3
- package/src/assets/web-panel/assets/{NotificationSettings-Dhg3Nktv.js → NotificationSettings-JvnQdStO.js} +1 -1
- package/src/assets/web-panel/assets/{OrderTableRenderer-QsAPcTb2.js → OrderTableRenderer-BMGL9RPJ.js} +1 -1
- package/src/assets/web-panel/assets/{Organization-BuiaUPkv.js → Organization-sBZnIb2D.js} +4 -4
- package/src/assets/web-panel/assets/{Overflow-C3jb6knl.js → Overflow-CzOhNGE6.js} +1 -1
- package/src/assets/web-panel/assets/{P2P-BOMyrLkb.js → P2P-D_D2yqda.js} +2 -2
- package/src/assets/web-panel/assets/{PdhVaultBrowser-UvLStCgS.js → PdhVaultBrowser-D5lKd_Kx.js} +3 -3
- package/src/assets/web-panel/assets/{Permissions-B9cN0dJH.js → Permissions-L5s-wENg.js} +4 -4
- package/src/assets/web-panel/assets/{PersonalDataHub-DNjW5qro.js → PersonalDataHub-iI49x6cH.js} +3 -3
- package/src/assets/web-panel/assets/{Pipeline-CFLyZ6Sh.js → Pipeline-DPHqyk09.js} +1 -1
- package/src/assets/web-panel/assets/{Privacy-D3w6vOxf.js → Privacy-B-ZL9360.js} +1 -1
- package/src/assets/web-panel/assets/{ProjectInit-cwcVsn8K.js → ProjectInit-f05YzsN2.js} +2 -2
- package/src/assets/web-panel/assets/{ProjectSettings-aUoAroc-.js → ProjectSettings-DZLKS6ex.js} +2 -2
- package/src/assets/web-panel/assets/{Projects-CrzQv7Zv.js → Projects-ButF3DyM.js} +1 -1
- package/src/assets/web-panel/assets/{Providers-jZaPizKR.js → Providers-CFDSXnkR.js} +1 -1
- package/src/assets/web-panel/assets/{QrScannerModal-DNzmwcfg.js → QrScannerModal-1N6Sdxdm.js} +1 -1
- package/src/assets/web-panel/assets/{QuickAsk-m-5Gwt70.js → QuickAsk-tEqRwRSR.js} +1 -1
- package/src/assets/web-panel/assets/{Recommend-CrrAjNxP.js → Recommend-D4ZVHIwz.js} +1 -1
- package/src/assets/web-panel/assets/{RemoteSession-BDCMjRFe.js → RemoteSession-Cqm32lhm.js} +2 -2
- package/src/assets/web-panel/assets/{Reputation-CPCt1Jc8.js → Reputation-BnjVsU_a.js} +1 -1
- package/src/assets/web-panel/assets/{Row-C-RorxHs.js → Row-DzpHyTuz.js} +1 -1
- package/src/assets/web-panel/assets/{RssFeed-CnQs4TuY.js → RssFeed-Du5-ouwx.js} +3 -3
- package/src/assets/web-panel/assets/{Search-B1jkeFZY.js → Search-CYztyf2o.js} +1 -1
- package/src/assets/web-panel/assets/{Security-CGKI3vvd.js → Security-C1hVxy8Z.js} +3 -3
- package/src/assets/web-panel/assets/{Services-DQjesYF8.js → Services-BscEbAZ2.js} +2 -2
- package/src/assets/web-panel/assets/{Skeleton-C9Ipj0A6.js → Skeleton-B2sMLqKQ.js} +1 -1
- package/src/assets/web-panel/assets/{Skills-D2j54i9O.js → Skills-CoHKax06.js} +1 -1
- package/src/assets/web-panel/assets/{Sla-xhrVjTeb.js → Sla-Bpie5KO6.js} +1 -1
- package/src/assets/web-panel/assets/{SpeechSettings-C6KMOUCa.js → SpeechSettings-BX63ijn5.js} +1 -1
- package/src/assets/web-panel/assets/{SyncSettings-CmIPU5wR.js → SyncSettings-Bf72so5A.js} +2 -2
- package/src/assets/web-panel/assets/{Tasks-B-T1J42r.js → Tasks-CVB2PZk4.js} +1 -1
- package/src/assets/web-panel/assets/{Templates-Aq669sjw.js → Templates-q0qyclJx.js} +1 -1
- package/src/assets/web-panel/assets/{Tenant-BKWUWNXB.js → Tenant-CW-W-i0k.js} +1 -1
- package/src/assets/web-panel/assets/{Terminal-DEeAvA8L.js → Terminal-D7QoPiXQ.js} +2 -2
- package/src/assets/web-panel/assets/{TimelineRenderer-C465ySdn.js → TimelineRenderer-BaRDAbyA.js} +1 -1
- package/src/assets/web-panel/assets/{Tokens-BgVqF-bX.js → Tokens-D6U4PRW8.js} +1 -1
- package/src/assets/web-panel/assets/{Trigger-BMNHmxwH.js → Trigger-ClBDWdLo.js} +1 -1
- package/src/assets/web-panel/assets/{Trust-B_adk89L.js → Trust-C_M6rWPq.js} +1 -1
- package/src/assets/web-panel/assets/{UkeySign-Biw38mog.js → UkeySign-bXHx8s4C.js} +1 -1
- package/src/assets/web-panel/assets/{VideoEditing-AC-eaw5k.js → VideoEditing-BcIt_ieI.js} +1 -1
- package/src/assets/web-panel/assets/{Wallet-8lQ5_t0X.js → Wallet-Dd-4jcLs.js} +4 -4
- package/src/assets/web-panel/assets/{WebAuthn-JDmH_-DF.js → WebAuthn-B4F6yxQK.js} +5 -5
- package/src/assets/web-panel/assets/{WorkflowEditor-DHQAdITG.js → WorkflowEditor-BEHkpfJq.js} +1 -1
- package/src/assets/web-panel/assets/{chat-C3qauUVv.js → chat-D7hhRpIO.js} +1 -1
- package/src/assets/web-panel/assets/{colors-DLP70JGe.js → colors-AMRhrRzS.js} +1 -1
- package/src/assets/web-panel/assets/{compact-item-DjsoqA5t.js → compact-item-BYpsOET5.js} +1 -1
- package/src/assets/web-panel/assets/{createContext-CfEdl1in.js → createContext-CoUkR4LQ.js} +1 -1
- package/src/assets/web-panel/assets/devWarning-BRylvxzY.js +1 -0
- package/src/assets/web-panel/assets/{hasIn-DWwHHlBG.js → hasIn-CJKJMKki.js} +1 -1
- package/src/assets/web-panel/assets/{index-Dfka-vzJ.js → index-8pmZHnQU.js} +1 -1
- package/src/assets/web-panel/assets/{index-DgQ92xdO.js → index-BBJFiZ9Y.js} +1 -1
- package/src/assets/web-panel/assets/{index-VtCz3A64.js → index-BP69Ul3S.js} +1 -1
- package/src/assets/web-panel/assets/{index-C8wrUFzY.js → index-BTFHYauX.js} +1 -1
- package/src/assets/web-panel/assets/{index-D5VL0NYy.js → index-BUJt2ar4.js} +1 -1
- package/src/assets/web-panel/assets/{index-B-rD2rhi.js → index-Bj30Aq6h.js} +1 -1
- package/src/assets/web-panel/assets/{index-gu8zBhtp.js → index-BlpuqGt5.js} +1 -1
- package/src/assets/web-panel/assets/index-Bzvxvzlk.js +1 -0
- package/src/assets/web-panel/assets/{index-CgruUvB3.js → index-C19xZKIP.js} +1 -1
- package/src/assets/web-panel/assets/{index-DTEcPJGf.js → index-C6bg22vc.js} +1 -1
- package/src/assets/web-panel/assets/{index-C4k6JGm_.js → index-CH6XEWdj.js} +1 -1
- package/src/assets/web-panel/assets/{index-C0xMzvk8.js → index-CK1Kknpl.js} +1 -1
- package/src/assets/web-panel/assets/{index-UEbYl2pu.js → index-CNyLbBGj.js} +1 -1
- package/src/assets/web-panel/assets/{index-Dmt1HDWZ.js → index-COg__P6M.js} +19 -19
- package/src/assets/web-panel/assets/{index-ArZCi1rK.js → index-CRlJOWsf.js} +1 -1
- package/src/assets/web-panel/assets/{index-BqPuyN4p.js → index-CScG8TAY.js} +1 -1
- package/src/assets/web-panel/assets/{index-B4gCFHtB.js → index-CdL9cQIE.js} +1 -1
- package/src/assets/web-panel/assets/{index-C47EiQ7d.js → index-Ck35u_0v.js} +1 -1
- package/src/assets/web-panel/assets/{index-Dn2rNMmv.js → index-CkLZQtsh.js} +1 -1
- package/src/assets/web-panel/assets/{index-T5emAyFT.js → index-DFoujkWD.js} +1 -1
- package/src/assets/web-panel/assets/{index-DbFnfyKh.js → index-DFwh0R9h.js} +1 -1
- package/src/assets/web-panel/assets/{index-CsEk9s04.js → index-DStN9E7o.js} +1 -1
- package/src/assets/web-panel/assets/{index-DTDpF8MS.js → index-DcozYrXV.js} +1 -1
- package/src/assets/web-panel/assets/{index-_g5q2hF3.js → index-De5mYp86.js} +1 -1
- package/src/assets/web-panel/assets/{index-Bp_WNm2_.js → index-DfEVOzNb.js} +1 -1
- package/src/assets/web-panel/assets/{index-OfMwkFIo.js → index-Dfna8sKa.js} +1 -1
- package/src/assets/web-panel/assets/{index-BcDeMCUK.js → index-DjbMaBLf.js} +1 -1
- package/src/assets/web-panel/assets/index-Dl6H3dxS.js +1 -0
- package/src/assets/web-panel/assets/{index-D-te-S9f.js → index-Dpl_SJuk.js} +1 -1
- package/src/assets/web-panel/assets/{index-DKsWujXP.js → index-DyF9uYzB.js} +1 -1
- package/src/assets/web-panel/assets/{index-BydBh7oT.js → index-HF3GvATm.js} +1 -1
- package/src/assets/web-panel/assets/{index-DAcBi6F-.js → index-OJmXGn8n.js} +1 -1
- package/src/assets/web-panel/assets/{index-CNeLedtU.js → index-ZPdNPhUq.js} +1 -1
- package/src/assets/web-panel/assets/{index-Ds1BfT_C.js → index-djikyGz3.js} +1 -1
- package/src/assets/web-panel/assets/{index-CbF27GqZ.js → index-gQWWnP3_.js} +1 -1
- package/src/assets/web-panel/assets/{index-CnhJbWsl.js → index-owVbLJnH.js} +1 -1
- package/src/assets/web-panel/assets/{index-Lh0e2afo.js → index-tWEDZHzj.js} +1 -1
- package/src/assets/web-panel/assets/{index-DbjUePgq.js → index-ttTl376q.js} +1 -1
- package/src/assets/web-panel/assets/{index-nuZaMsMy.js → index-z5rYvstM.js} +1 -1
- package/src/assets/web-panel/assets/{initDefaultProps-C_pMmi75.js → initDefaultProps-B_Uk-wzg.js} +1 -1
- package/src/assets/web-panel/assets/{motion-BvKgS96o.js → motion-ez7H8y8b.js} +1 -1
- package/src/assets/web-panel/assets/{move-C-1U4R1I.js → move-CKWUhhhX.js} +1 -1
- package/src/assets/web-panel/assets/{mtc-parser-D6BC968-.js → mtc-parser-DkNl7ocj.js} +1 -1
- package/src/assets/web-panel/assets/{omit-DxOZvcGQ.js → omit-C0uA2ytw.js} +1 -1
- package/src/assets/web-panel/assets/{pickAttrs-LpzmHdkw.js → pickAttrs-CySaWMPl.js} +1 -1
- package/src/assets/web-panel/assets/{placementArrow-Bm--qom1.js → placementArrow-BWL3-3xo.js} +1 -1
- package/src/assets/web-panel/assets/{responsiveObserve-CVZZlhJf.js → responsiveObserve-BRy_wJog.js} +1 -1
- package/src/assets/web-panel/assets/{slide-DD1xINGx.js → slide-DmmvSaBD.js} +1 -1
- package/src/assets/web-panel/assets/{statusUtils-LrjHAy63.js → statusUtils-C_7SYY0K.js} +1 -1
- package/src/assets/web-panel/assets/{styleChecker-BqknYxTe.js → styleChecker-DYgBJGDh.js} +1 -1
- package/src/assets/web-panel/assets/{useFlexGapSupport-Dljy3diT.js → useFlexGapSupport-nPkoZkT6.js} +1 -1
- package/src/assets/web-panel/assets/{useFs-OedUtPLc.js → useFs-CzpsfP4_.js} +1 -1
- package/src/assets/web-panel/assets/{usePersonalDataHub-Cz-kxbP_.js → usePersonalDataHub-Bo2vvMWl.js} +1 -1
- package/src/assets/web-panel/assets/{vnode-C8ZuTk8W.js → vnode-D-hXAnHb.js} +1 -1
- package/src/assets/web-panel/assets/{zoom-CjbgJYXQ.js → zoom-DW7seTGG.js} +1 -1
- package/src/assets/web-panel/index.html +1 -1
- package/src/commands/agent.js +39 -14
- package/src/commands/background-session.js +12 -1
- package/src/commands/permissions.js +24 -1
- package/src/gateways/ws/ws-agent-handler.js +26 -1
- package/src/gateways/ws/ws-approval-gate.js +131 -0
- package/src/index.js +63 -845
- package/src/lib/agent-ipc-bus.js +331 -0
- package/src/lib/agent-worktree.js +173 -5
- package/src/lib/background-agent-supervisor.js +193 -4
- package/src/lib/context-source-ledger.js +164 -0
- package/src/lib/execution-trace/index.js +10 -0
- package/src/lib/execution-trace/metrics-collector.js +218 -0
- package/src/lib/execution-trace/trace-context.js +182 -0
- package/src/lib/hook-manager.js +17 -0
- package/src/lib/hooks-v2-runtime.js +278 -0
- package/src/lib/interaction-adapter.js +26 -5
- package/src/lib/metrics-collector.js +127 -0
- package/src/lib/observability/index.js +33 -0
- package/src/lib/observability/otlp-exporter.js +260 -0
- package/src/lib/otlp-exporter.js +45 -0
- package/src/lib/process-execution-broker/SPAWN_AUDIT_REPORT.md +117 -0
- package/src/lib/process-execution-broker/index.js +450 -0
- package/src/lib/process-execution-broker/patch-child-process.js +50 -0
- package/src/lib/process-execution-broker/spawn-audit.json +3814 -0
- package/src/lib/repl-rewind.js +62 -14
- package/src/lib/runtime-provenance-ledger.js +190 -0
- package/src/lib/session-hooks.js +41 -0
- package/src/lib/turn-binding.js +134 -0
- package/src/program-base.js +4 -1
- package/src/repl/agent-repl.js +77 -3
- package/src/repl/repl-turn-binding.js +91 -0
- package/src/runtime/headless-runner.js +59 -0
- package/src/assets/web-panel/assets/MarkdownRenderer-RQ1HbTY5.js +0 -1
- package/src/assets/web-panel/assets/devWarning-Cwuuqc_f.js +0 -1
- package/src/assets/web-panel/assets/index-BkTk-zJK.js +0 -1
- package/src/assets/web-panel/assets/index-C-BSTEhe.js +0 -1
|
@@ -0,0 +1,331 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Agent IPC Bus — M2: 后台Agent实时交互总线
|
|
3
|
+
* 对应文档 §2.2
|
|
4
|
+
*
|
|
5
|
+
* 解决后台agent运行期间不能提问/confirm/elicit的问题:
|
|
6
|
+
* - child agent 通过 IPC 发送 interaction_request
|
|
7
|
+
* - parent 路由给 UI/REPL/headless resolver
|
|
8
|
+
* - 结果通过 interaction_response 返回child
|
|
9
|
+
* - 支持超时、取消、默认值
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
import { EventEmitter } from "node:events";
|
|
13
|
+
import crypto from "node:crypto";
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* @typedef {Object} InteractionRequest
|
|
17
|
+
* @property {string} requestId
|
|
18
|
+
* @property {string} agentId - child agent id
|
|
19
|
+
* @property {string} sessionId
|
|
20
|
+
* @property {string} turnId
|
|
21
|
+
* @property {'permission_prompt'|'human_input'|'question'|'confirm'|'elicit'|'mcp_elicit'} type
|
|
22
|
+
* @property {string} prompt
|
|
23
|
+
* @property {string[]} [choices]
|
|
24
|
+
* @property {any} [defaultValue]
|
|
25
|
+
* @property {number} [timeoutMs]
|
|
26
|
+
* @property {Object} [metadata]
|
|
27
|
+
*/
|
|
28
|
+
|
|
29
|
+
class AgentIPCBus extends EventEmitter {
|
|
30
|
+
constructor() {
|
|
31
|
+
super();
|
|
32
|
+
/** @type {Map<string, {req: InteractionRequest, resolve: Function, reject: Function, timer?: NodeJS.Timeout}>} */
|
|
33
|
+
this._pendingRequests = new Map();
|
|
34
|
+
/** @type {Map<string, Function>} resolvers by agentId */
|
|
35
|
+
this._agentResolvers = new Map();
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Register an agent worker's send function so bus can route responses back
|
|
40
|
+
* @param {string} agentId
|
|
41
|
+
* @param {(msg: any) => void} sendToWorker
|
|
42
|
+
*/
|
|
43
|
+
registerAgent(agentId, sendToWorker) {
|
|
44
|
+
this._agentResolvers.set(agentId, sendToWorker);
|
|
45
|
+
this.emit("agent:registered", { agentId });
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
unregisterAgent(agentId) {
|
|
49
|
+
this._agentResolvers.delete(agentId);
|
|
50
|
+
// Reject all pending for this agent
|
|
51
|
+
for (const [reqId, entry] of this._pendingRequests.entries()) {
|
|
52
|
+
if (entry.req.agentId === agentId) {
|
|
53
|
+
if (entry.timer) clearTimeout(entry.timer);
|
|
54
|
+
entry.reject(new Error(`Agent ${agentId} disconnected while waiting for response`));
|
|
55
|
+
this._pendingRequests.delete(reqId);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
this.emit("agent:unregistered", { agentId });
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Child agent calls this to request human interaction
|
|
63
|
+
* @param {string} agentId
|
|
64
|
+
* @param {Partial<InteractionRequest>} req
|
|
65
|
+
* @returns {Promise<any>}
|
|
66
|
+
*/
|
|
67
|
+
requestInteraction(agentId, req) {
|
|
68
|
+
const requestId = crypto.randomUUID();
|
|
69
|
+
const fullReq = {
|
|
70
|
+
requestId,
|
|
71
|
+
agentId,
|
|
72
|
+
sessionId: req.sessionId,
|
|
73
|
+
turnId: req.turnId,
|
|
74
|
+
type: req.type || "question",
|
|
75
|
+
prompt: req.prompt,
|
|
76
|
+
choices: req.choices,
|
|
77
|
+
defaultValue: req.defaultValue,
|
|
78
|
+
timeoutMs: req.timeoutMs || 300000, // 5min default
|
|
79
|
+
metadata: req.metadata || {},
|
|
80
|
+
};
|
|
81
|
+
|
|
82
|
+
return new Promise((resolve, reject) => {
|
|
83
|
+
const timer = setTimeout(() => {
|
|
84
|
+
this._pendingRequests.delete(requestId);
|
|
85
|
+
if (fullReq.defaultValue !== undefined) {
|
|
86
|
+
resolve(fullReq.defaultValue);
|
|
87
|
+
} else {
|
|
88
|
+
reject(new Error(`Interaction request ${requestId} timed out after ${fullReq.timeoutMs}ms`));
|
|
89
|
+
}
|
|
90
|
+
this.emit("request:timeout", fullReq);
|
|
91
|
+
}, fullReq.timeoutMs);
|
|
92
|
+
|
|
93
|
+
this._pendingRequests.set(requestId, { req: fullReq, resolve, reject, timer });
|
|
94
|
+
this.emit("request", fullReq);
|
|
95
|
+
});
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* Parent/UI calls this to respond to a pending request
|
|
100
|
+
* @param {string} requestId
|
|
101
|
+
* @param {any} response
|
|
102
|
+
*/
|
|
103
|
+
respond(requestId, response) {
|
|
104
|
+
const entry = this._pendingRequests.get(requestId);
|
|
105
|
+
if (!entry) return false;
|
|
106
|
+
if (entry.timer) clearTimeout(entry.timer);
|
|
107
|
+
this._pendingRequests.delete(requestId);
|
|
108
|
+
entry.resolve(response);
|
|
109
|
+
this.emit("request:resolved", { requestId, response });
|
|
110
|
+
return true;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* Reject/cancel a pending request
|
|
115
|
+
* @param {string} requestId
|
|
116
|
+
* @param {Error} [err]
|
|
117
|
+
*/
|
|
118
|
+
cancel(requestId, err) {
|
|
119
|
+
const entry = this._pendingRequests.get(requestId);
|
|
120
|
+
if (!entry) return false;
|
|
121
|
+
if (entry.timer) clearTimeout(entry.timer);
|
|
122
|
+
this._pendingRequests.delete(requestId);
|
|
123
|
+
entry.reject(err || new Error("Interaction cancelled by user"));
|
|
124
|
+
this.emit("request:cancelled", { requestId });
|
|
125
|
+
return true;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/** Get pending interaction requests for UI */
|
|
129
|
+
getPendingRequests() {
|
|
130
|
+
return Array.from(this._pendingRequests.values()).map(e => e.req);
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
/** Number of pending requests */
|
|
134
|
+
get pendingCount() {
|
|
135
|
+
return this._pendingRequests.size;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
/**
|
|
139
|
+
* Spawn a child agent process with standardized JSON-RPC over stdio protocol
|
|
140
|
+
* Aligned with Claude Code sub-agent spawn specification
|
|
141
|
+
* @param {string} command - Agent executable/script path
|
|
142
|
+
* @param {string[]} args - Command arguments
|
|
143
|
+
* @param {object} options - Spawn options
|
|
144
|
+
* @returns {Promise<{process: ChildProcess, agentId: string}>}
|
|
145
|
+
*/
|
|
146
|
+
async spawnAgentProcess(command, args = [], options = {}) {
|
|
147
|
+
const { spawn } = await import("node:child_process");
|
|
148
|
+
const agentId = options.agentId || crypto.randomUUID();
|
|
149
|
+
const heartbeatMs = options.heartbeatMs || 30000;
|
|
150
|
+
|
|
151
|
+
return new Promise((resolve, reject) => {
|
|
152
|
+
const child = spawn(command, args, {
|
|
153
|
+
stdio: ["pipe", "pipe", "pipe"],
|
|
154
|
+
env: {
|
|
155
|
+
...process.env,
|
|
156
|
+
CHAINLESSCHAIN_AGENT_ID: agentId,
|
|
157
|
+
CHAINLESSCHAIN_AGENT_MODE: "subagent",
|
|
158
|
+
CHAINLESSCHAIN_IPC_PROTOCOL: "jsonrpc-stdio-v1",
|
|
159
|
+
...options.env,
|
|
160
|
+
},
|
|
161
|
+
...options.spawnOptions,
|
|
162
|
+
});
|
|
163
|
+
|
|
164
|
+
let buffer = "";
|
|
165
|
+
let initialized = false;
|
|
166
|
+
let heartbeatTimer = null;
|
|
167
|
+
|
|
168
|
+
// Handle stdout: line-delimited JSON-RPC
|
|
169
|
+
child.stdout.on("data", (chunk) => {
|
|
170
|
+
buffer += chunk.toString("utf8");
|
|
171
|
+
const lines = buffer.split("\n");
|
|
172
|
+
buffer = lines.pop() || ""; // Keep incomplete line in buffer
|
|
173
|
+
|
|
174
|
+
for (const line of lines) {
|
|
175
|
+
if (!line.trim()) continue;
|
|
176
|
+
try {
|
|
177
|
+
const msg = JSON.parse(line);
|
|
178
|
+
this._handleIncomingMessage(agentId, msg, child);
|
|
179
|
+
if (msg.method === "initialize" && !initialized) {
|
|
180
|
+
initialized = true;
|
|
181
|
+
this.registerAgent(agentId, (outMsg) => {
|
|
182
|
+
child.stdin.write(JSON.stringify(outMsg) + "\n");
|
|
183
|
+
});
|
|
184
|
+
// Send initialize response
|
|
185
|
+
child.stdin.write(JSON.stringify({
|
|
186
|
+
jsonrpc: "2.0",
|
|
187
|
+
id: msg.id,
|
|
188
|
+
result: {
|
|
189
|
+
protocolVersion: "1.0",
|
|
190
|
+
agentId,
|
|
191
|
+
capabilities: { interaction: true, tools: true },
|
|
192
|
+
},
|
|
193
|
+
}) + "\n");
|
|
194
|
+
// Setup heartbeat
|
|
195
|
+
heartbeatTimer = setInterval(() => {
|
|
196
|
+
if (child.exitCode !== null) {
|
|
197
|
+
clearInterval(heartbeatTimer);
|
|
198
|
+
return;
|
|
199
|
+
}
|
|
200
|
+
child.stdin.write(JSON.stringify({ jsonrpc: "2.0", method: "heartbeat" }) + "\n");
|
|
201
|
+
}, heartbeatMs);
|
|
202
|
+
resolve({ process: child, agentId });
|
|
203
|
+
}
|
|
204
|
+
} catch (e) {
|
|
205
|
+
this.emit("protocol:error", { agentId, error: e, raw: line });
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
});
|
|
209
|
+
|
|
210
|
+
// Handle stderr: pass through to logger
|
|
211
|
+
child.stderr.on("data", (chunk) => {
|
|
212
|
+
this.emit("agent:stderr", { agentId, data: chunk.toString("utf8") });
|
|
213
|
+
if (options.captureStderr !== false) {
|
|
214
|
+
process.stderr.write(`[agent:${agentId}] ${chunk}`);
|
|
215
|
+
}
|
|
216
|
+
});
|
|
217
|
+
|
|
218
|
+
child.on("error", (err) => {
|
|
219
|
+
if (heartbeatTimer) clearInterval(heartbeatTimer);
|
|
220
|
+
if (!initialized) reject(err);
|
|
221
|
+
this.emit("agent:error", { agentId, error: err });
|
|
222
|
+
});
|
|
223
|
+
|
|
224
|
+
child.on("exit", (code, signal) => {
|
|
225
|
+
if (heartbeatTimer) clearInterval(heartbeatTimer);
|
|
226
|
+
this.unregisterAgent(agentId);
|
|
227
|
+
this.emit("agent:exit", { agentId, code, signal });
|
|
228
|
+
});
|
|
229
|
+
|
|
230
|
+
// Timeout for initialization
|
|
231
|
+
setTimeout(() => {
|
|
232
|
+
if (!initialized) {
|
|
233
|
+
child.kill();
|
|
234
|
+
reject(new Error(`Agent ${agentId} initialization timed out`));
|
|
235
|
+
}
|
|
236
|
+
}, options.initTimeoutMs || 10000);
|
|
237
|
+
});
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
/**
|
|
241
|
+
* Handle incoming JSON-RPC message from child agent
|
|
242
|
+
* @private
|
|
243
|
+
*/
|
|
244
|
+
_handleIncomingMessage(agentId, msg, child) {
|
|
245
|
+
if (!msg.jsonrpc || msg.jsonrpc !== "2.0") {
|
|
246
|
+
this.emit("protocol:warning", { agentId, msg, reason: "missing jsonrpc version" });
|
|
247
|
+
return;
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
// Handle requests (method + id)
|
|
251
|
+
if (msg.method && msg.id) {
|
|
252
|
+
if (msg.method === "interaction_request") {
|
|
253
|
+
this.requestInteraction(agentId, msg.params).then((result) => {
|
|
254
|
+
child.stdin.write(JSON.stringify({
|
|
255
|
+
jsonrpc: "2.0",
|
|
256
|
+
id: msg.id,
|
|
257
|
+
result,
|
|
258
|
+
}) + "\n");
|
|
259
|
+
}).catch((err) => {
|
|
260
|
+
child.stdin.write(JSON.stringify({
|
|
261
|
+
jsonrpc: "2.0",
|
|
262
|
+
id: msg.id,
|
|
263
|
+
error: { code: -32000, message: err.message },
|
|
264
|
+
}) + "\n");
|
|
265
|
+
});
|
|
266
|
+
} else if (msg.method === "log") {
|
|
267
|
+
this.emit("agent:log", { agentId, params: msg.params });
|
|
268
|
+
// Acknowledge log
|
|
269
|
+
child.stdin.write(JSON.stringify({ jsonrpc: "2.0", id: msg.id, result: true }) + "\n");
|
|
270
|
+
} else {
|
|
271
|
+
this.emit("agent:request", { agentId, msg });
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
// Handle notifications (method, no id)
|
|
275
|
+
else if (msg.method && !msg.id) {
|
|
276
|
+
if (msg.method === "heartbeat") {
|
|
277
|
+
this.emit("agent:heartbeat", { agentId });
|
|
278
|
+
} else if (msg.method === "progress") {
|
|
279
|
+
this.emit("agent:progress", { agentId, params: msg.params });
|
|
280
|
+
} else {
|
|
281
|
+
this.emit("agent:notification", { agentId, method: msg.method, params: msg.params });
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
// Handle responses (id + result/error)
|
|
285
|
+
else if (msg.id) {
|
|
286
|
+
this.emit("agent:response", { agentId, msg });
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
/**
|
|
291
|
+
* Send a JSON-RPC request to a spawned agent via stdio
|
|
292
|
+
* @param {string} agentId
|
|
293
|
+
* @param {string} method
|
|
294
|
+
* @param {any} params
|
|
295
|
+
* @param {number} [timeoutMs=30000]
|
|
296
|
+
* @returns {Promise<any>}
|
|
297
|
+
*/
|
|
298
|
+
sendRequest(agentId, method, params = {}, timeoutMs = 30000) {
|
|
299
|
+
const sendToWorker = this._agentResolvers.get(agentId);
|
|
300
|
+
if (!sendToWorker) {
|
|
301
|
+
return Promise.reject(new Error(`Agent ${agentId} not registered`));
|
|
302
|
+
}
|
|
303
|
+
const id = crypto.randomUUID();
|
|
304
|
+
return new Promise((resolve, reject) => {
|
|
305
|
+
const timer = setTimeout(() => {
|
|
306
|
+
this.removeAllListeners(`response:${id}`);
|
|
307
|
+
reject(new Error(`Request ${method} to agent ${agentId} timed out`));
|
|
308
|
+
}, timeoutMs);
|
|
309
|
+
|
|
310
|
+
const onResponse = (respMsg) => {
|
|
311
|
+
if (respMsg.msg.id === id) {
|
|
312
|
+
clearTimeout(timer);
|
|
313
|
+
this.off("agent:response", onResponse);
|
|
314
|
+
if (respMsg.msg.error) {
|
|
315
|
+
reject(new Error(respMsg.msg.error.message));
|
|
316
|
+
} else {
|
|
317
|
+
resolve(respMsg.msg.result);
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
};
|
|
321
|
+
this.on("agent:response", onResponse);
|
|
322
|
+
|
|
323
|
+
sendToWorker({ jsonrpc: "2.0", id, method, params });
|
|
324
|
+
});
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
// Singleton
|
|
329
|
+
const ipcBus = new AgentIPCBus();
|
|
330
|
+
export default ipcBus;
|
|
331
|
+
export { AgentIPCBus };
|
|
@@ -11,11 +11,18 @@
|
|
|
11
11
|
* a merge hint. Removal failures fail-open to "kept".
|
|
12
12
|
*/
|
|
13
13
|
|
|
14
|
-
import { execSync } from "child_process";
|
|
14
|
+
import { execFileSync, execSync } from "child_process";
|
|
15
|
+
import { existsSync, realpathSync } from "node:fs";
|
|
16
|
+
import { resolve } from "node:path";
|
|
15
17
|
import { createWorktree, removeWorktree } from "./worktree-isolator.js";
|
|
16
18
|
import { findProjectRoot } from "./project-instructions.js";
|
|
17
19
|
|
|
18
|
-
export const _deps = {
|
|
20
|
+
export const _deps = {
|
|
21
|
+
execFileSync,
|
|
22
|
+
execSync,
|
|
23
|
+
createWorktree,
|
|
24
|
+
removeWorktree,
|
|
25
|
+
};
|
|
19
26
|
|
|
20
27
|
function git(cmd, cwd, deps) {
|
|
21
28
|
return (deps.execSync || execSync)(`git ${cmd}`, {
|
|
@@ -25,6 +32,132 @@ function git(cmd, cwd, deps) {
|
|
|
25
32
|
});
|
|
26
33
|
}
|
|
27
34
|
|
|
35
|
+
function canonicalPath(value) {
|
|
36
|
+
const absolute = resolve(String(value || ""));
|
|
37
|
+
const real = realpathSync.native
|
|
38
|
+
? realpathSync.native(absolute)
|
|
39
|
+
: realpathSync(absolute);
|
|
40
|
+
return process.platform === "win32" ? real.toLowerCase() : real;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
function parseRegisteredWorktrees(text) {
|
|
44
|
+
const entries = [];
|
|
45
|
+
let current = null;
|
|
46
|
+
for (const line of String(text || "").split(/\r?\n/)) {
|
|
47
|
+
if (line.startsWith("worktree ")) {
|
|
48
|
+
if (current) entries.push(current);
|
|
49
|
+
current = { path: line.slice("worktree ".length) };
|
|
50
|
+
continue;
|
|
51
|
+
}
|
|
52
|
+
if (!current) continue;
|
|
53
|
+
if (line.startsWith("HEAD ")) current.head = line.slice("HEAD ".length);
|
|
54
|
+
if (line.startsWith("branch ")) {
|
|
55
|
+
current.branch = line.slice("branch ".length);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
if (current) entries.push(current);
|
|
59
|
+
return entries;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Verify that persisted worktree metadata still identifies the same linked
|
|
64
|
+
* worktree. Cleanup callers use this as a fail-closed identity check before
|
|
65
|
+
* invoking any removal primitive: a missing/corrupt path must never turn into
|
|
66
|
+
* a recursive delete of the repository checkout.
|
|
67
|
+
*
|
|
68
|
+
* @returns {{valid:boolean, reason:string}}
|
|
69
|
+
*/
|
|
70
|
+
export function validateAgentWorktree(info, { deps = _deps } = {}) {
|
|
71
|
+
const worktreePath = info?.path || info?.worktreePath;
|
|
72
|
+
if (!worktreePath || !info?.repoRoot || !info?.branch || !info?.baseSha) {
|
|
73
|
+
return { valid: false, reason: "incomplete worktree metadata" };
|
|
74
|
+
}
|
|
75
|
+
if (!/^[0-9a-f]{40,64}$/i.test(String(info.baseSha))) {
|
|
76
|
+
return { valid: false, reason: "invalid worktree base SHA" };
|
|
77
|
+
}
|
|
78
|
+
if (!existsSync(worktreePath) || !existsSync(info.repoRoot)) {
|
|
79
|
+
return { valid: false, reason: "worktree or repository path is missing" };
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
try {
|
|
83
|
+
const repo = canonicalPath(info.repoRoot);
|
|
84
|
+
const worktree = canonicalPath(worktreePath);
|
|
85
|
+
if (repo === worktree) {
|
|
86
|
+
return {
|
|
87
|
+
valid: false,
|
|
88
|
+
reason: "refusing to treat the main checkout as an agent worktree",
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
const repoTop = canonicalPath(
|
|
93
|
+
git("rev-parse --show-toplevel", info.repoRoot, deps).trim(),
|
|
94
|
+
);
|
|
95
|
+
const worktreeTop = canonicalPath(
|
|
96
|
+
git("rev-parse --show-toplevel", worktreePath, deps).trim(),
|
|
97
|
+
);
|
|
98
|
+
if (repoTop !== repo || worktreeTop !== worktree) {
|
|
99
|
+
return { valid: false, reason: "worktree path identity mismatch" };
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
const registered = parseRegisteredWorktrees(
|
|
103
|
+
git("worktree list --porcelain", info.repoRoot, deps),
|
|
104
|
+
).find((entry) => {
|
|
105
|
+
try {
|
|
106
|
+
return canonicalPath(entry.path) === worktree;
|
|
107
|
+
} catch {
|
|
108
|
+
return false;
|
|
109
|
+
}
|
|
110
|
+
});
|
|
111
|
+
if (!registered) {
|
|
112
|
+
return {
|
|
113
|
+
valid: false,
|
|
114
|
+
reason: "path is not a registered worktree of the repository",
|
|
115
|
+
};
|
|
116
|
+
}
|
|
117
|
+
if (registered.branch !== `refs/heads/${info.branch}`) {
|
|
118
|
+
return {
|
|
119
|
+
valid: false,
|
|
120
|
+
reason: "registered worktree branch does not match persisted metadata",
|
|
121
|
+
};
|
|
122
|
+
}
|
|
123
|
+
return { valid: true, reason: "verified" };
|
|
124
|
+
} catch (error) {
|
|
125
|
+
return {
|
|
126
|
+
valid: false,
|
|
127
|
+
reason: `worktree identity could not be verified: ${error.message}`,
|
|
128
|
+
};
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
function removeVerifiedAgentWorktree(info, deps) {
|
|
133
|
+
// Unit seams historically inject removeWorktree directly. Keep that seam,
|
|
134
|
+
// while production uses a non-forced git removal: if the tree changed
|
|
135
|
+
// between inspection and deletion, git refuses instead of discarding it.
|
|
136
|
+
if (deps.removeWorktree && deps.removeWorktree !== removeWorktree) {
|
|
137
|
+
deps.removeWorktree(info.repoRoot, info.path);
|
|
138
|
+
return null;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
const run = deps.execFileSync || execFileSync;
|
|
142
|
+
run("git", ["worktree", "remove", info.path], {
|
|
143
|
+
cwd: info.repoRoot,
|
|
144
|
+
encoding: "utf8",
|
|
145
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
146
|
+
});
|
|
147
|
+
try {
|
|
148
|
+
run("git", ["branch", "-D", "--", info.branch], {
|
|
149
|
+
cwd: info.repoRoot,
|
|
150
|
+
encoding: "utf8",
|
|
151
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
152
|
+
});
|
|
153
|
+
return null;
|
|
154
|
+
} catch (error) {
|
|
155
|
+
// The worktree itself is already gone. Preserve an honest warning rather
|
|
156
|
+
// than claiming the whole cleanup failed and that the path was kept.
|
|
157
|
+
return `branch cleanup failed: ${error.message}`;
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
|
|
28
161
|
/**
|
|
29
162
|
* @returns {{ path, branch, repoRoot, baseSha }}
|
|
30
163
|
*/
|
|
@@ -51,22 +184,57 @@ export function setupAgentWorktree({
|
|
|
51
184
|
export function finishAgentWorktree(info, { deps = _deps } = {}) {
|
|
52
185
|
if (!info) return { removed: false, kept: false, reason: "no worktree" };
|
|
53
186
|
|
|
187
|
+
// Production cleanup validates the persisted path against git's own
|
|
188
|
+
// worktree registry. Custom dependency bundles are test seams and retain the
|
|
189
|
+
// historical pure mocked behavior unless they provide validateWorktree.
|
|
190
|
+
const validation =
|
|
191
|
+
typeof deps.validateWorktree === "function"
|
|
192
|
+
? deps.validateWorktree(info)
|
|
193
|
+
: deps === _deps
|
|
194
|
+
? validateAgentWorktree(info, { deps })
|
|
195
|
+
: { valid: true, reason: "injected validation seam" };
|
|
196
|
+
if (!validation?.valid) {
|
|
197
|
+
return {
|
|
198
|
+
removed: false,
|
|
199
|
+
kept: true,
|
|
200
|
+
reason: `unverifiable worktree: ${validation?.reason || "unknown reason"}`,
|
|
201
|
+
};
|
|
202
|
+
}
|
|
203
|
+
|
|
54
204
|
let dirty = true;
|
|
55
205
|
let headSha = null;
|
|
206
|
+
let readable = false;
|
|
56
207
|
try {
|
|
57
208
|
dirty = git("status --porcelain", info.path, deps).trim().length > 0;
|
|
58
209
|
headSha = git("rev-parse HEAD", info.path, deps).trim();
|
|
210
|
+
readable = true;
|
|
59
211
|
} catch {
|
|
60
212
|
/* unreadable → keep, never destroy work we cannot verify */
|
|
61
213
|
}
|
|
62
214
|
|
|
215
|
+
if (!readable) {
|
|
216
|
+
return {
|
|
217
|
+
removed: false,
|
|
218
|
+
kept: true,
|
|
219
|
+
reason: "unverifiable worktree: git state could not be read",
|
|
220
|
+
};
|
|
221
|
+
}
|
|
222
|
+
|
|
63
223
|
const hasCommits = headSha !== null && headSha !== info.baseSha;
|
|
64
224
|
if (!dirty && !hasCommits) {
|
|
65
225
|
try {
|
|
66
|
-
|
|
67
|
-
return {
|
|
226
|
+
const warning = removeVerifiedAgentWorktree(info, deps);
|
|
227
|
+
return {
|
|
228
|
+
removed: true,
|
|
229
|
+
kept: false,
|
|
230
|
+
reason: warning ? `no changes; ${warning}` : "no changes",
|
|
231
|
+
};
|
|
68
232
|
} catch (err) {
|
|
69
|
-
return {
|
|
233
|
+
return {
|
|
234
|
+
removed: false,
|
|
235
|
+
kept: true,
|
|
236
|
+
reason: `cleanup failed: ${err.message}`,
|
|
237
|
+
};
|
|
70
238
|
}
|
|
71
239
|
}
|
|
72
240
|
return {
|