chainlesschain 0.162.197 → 0.162.198
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/src/assets/web-panel/assets/{AIOps-D3Gu1a5d.js → AIOps-CSN-D2xb.js} +1 -1
- package/src/assets/web-panel/assets/{ActionButton-DrwAoKaz.js → ActionButton-CBSMlGzv.js} +1 -1
- package/src/assets/web-panel/assets/{Analytics-C7Q-gd_3.js → Analytics-3yr3Nt2t.js} +3 -3
- package/src/assets/web-panel/assets/{AppLayout-DgBtae-n.js → AppLayout-1b-o3lMH.js} +5 -5
- package/src/assets/web-panel/assets/{Artifacts-pQ6LcA-W.js → Artifacts-Dr8rJi17.js} +1 -1
- package/src/assets/web-panel/assets/{Audit-DK61tocB.js → Audit-2V689HC2.js} +1 -1
- package/src/assets/web-panel/assets/{BackgroundAgents-BOKgDFRh.js → BackgroundAgents-DIxkNrD_.js} +1 -1
- package/src/assets/web-panel/assets/{Backup-Bo9G4vTP.js → Backup-Pg6BcwrE.js} +1 -1
- package/src/assets/web-panel/assets/{BaseInput-Do_dE9_v.js → BaseInput-CLUpqyfH.js} +1 -1
- package/src/assets/web-panel/assets/{Chat-COVvZzOL.js → Chat-qVS8rD4G.js} +5 -5
- package/src/assets/web-panel/assets/ChatBubbleRenderer-ti49X12Q.js +1 -0
- package/src/assets/web-panel/assets/{Checkbox-Z9MJMolt.js → Checkbox-Bs9AaEay.js} +1 -1
- package/src/assets/web-panel/assets/{Codegen-BVzWKVbf.js → Codegen-Dvc1mq8q.js} +1 -1
- package/src/assets/web-panel/assets/{Col-C8MnYcbT.js → Col-C4_biO7A.js} +1 -1
- package/src/assets/web-panel/assets/{Community-DQxqw15l.js → Community-al25XQUV.js} +1 -1
- package/src/assets/web-panel/assets/{Compact-BAUmrh3y.js → Compact-D-ZDB8M1.js} +1 -1
- package/src/assets/web-panel/assets/{Compliance-jPAkQ3zK.js → Compliance-6lIfDz1W.js} +1 -1
- package/src/assets/web-panel/assets/{Cowork-BBQyxbh8.js → Cowork-DdI48XMJ.js} +3 -3
- package/src/assets/web-panel/assets/{Cron-W5koq8Z5.js → Cron-DF5HZd2g.js} +2 -2
- package/src/assets/web-panel/assets/{Crosschain-BQhnu5hS.js → Crosschain-DuxeSvt3.js} +1 -1
- package/src/assets/web-panel/assets/{DID-DD7PlI18.js → DID-B9yHkUTd.js} +2 -2
- package/src/assets/web-panel/assets/{Dashboard-w_ILuwKI.js → Dashboard-Z5EuI3IM.js} +2 -2
- package/src/assets/web-panel/assets/{Dropdown-qiDUAyAo.js → Dropdown-vuRfO3dY.js} +1 -1
- package/src/assets/web-panel/assets/{EmailListRenderer-PDeBcI5H.js → EmailListRenderer-QFLCc1uL.js} +1 -1
- package/src/assets/web-panel/assets/{FamilyGuardDashboard-SuArscRM.js → FamilyGuardDashboard-BZ7EXkFJ.js} +1 -1
- package/src/assets/web-panel/assets/{Federation-BCovZaUn.js → Federation-DycIEg2s.js} +1 -1
- package/src/assets/web-panel/assets/{FormItemContext-DjlFdVFk.js → FormItemContext-Ba0H_7yG.js} +1 -1
- package/src/assets/web-panel/assets/{GenericCardRenderer-DoEZ7fcS.js → GenericCardRenderer--JgSTK-C.js} +1 -1
- package/src/assets/web-panel/assets/{Git-CV0-6GPI.js → Git-DEvTbldE.js} +2 -2
- package/src/assets/web-panel/assets/{Governance-B_SFN50w.js → Governance-BJIljy18.js} +1 -1
- package/src/assets/web-panel/assets/{Inference-heI0Eo6k.js → Inference-CVyW3G3q.js} +1 -1
- package/src/assets/web-panel/assets/{KnowledgeGraph-Cz5INS5p.js → KnowledgeGraph-v8-borge.js} +1 -1
- package/src/assets/web-panel/assets/{Logs-BSFxiHkg.js → Logs-BCdS7gGo.js} +2 -2
- package/src/assets/web-panel/assets/MarkdownRenderer-CU-JB2Ns.js +1 -0
- package/src/assets/web-panel/assets/{Marketplace-0TWTpE7k.js → Marketplace-AN6tGA2B.js} +1 -1
- package/src/assets/web-panel/assets/{McpTools-CV4-wBLm.js → McpTools-DGViXASN.js} +4 -4
- package/src/assets/web-panel/assets/{Memory-x5bH5rw5.js → Memory-Bpvmx9X4.js} +2 -2
- package/src/assets/web-panel/assets/{MobileBridge-DaBmUu9G.js → MobileBridge-CTP-DOTC.js} +1 -1
- package/src/assets/web-panel/assets/MobileProjects-C8qfktxn.js +1 -0
- package/src/assets/web-panel/assets/{Mtc-B_zKEBif.js → Mtc-ndYr0M1s.js} +5 -5
- package/src/assets/web-panel/assets/{MtcAudit-BCfKD2yg.js → MtcAudit-Ci0-YK7W.js} +2 -2
- package/src/assets/web-panel/assets/{Multisig-DgYfj6c_.js → Multisig-BehSTFW7.js} +3 -3
- package/src/assets/web-panel/assets/{NLProgramming-Dz09lFUm.js → NLProgramming-B91_YBLv.js} +1 -1
- package/src/assets/web-panel/assets/{Notes-Cuj6ugBE.js → Notes-BQlJ0-gq.js} +3 -3
- package/src/assets/web-panel/assets/{NotificationSettings-DK2WXjhg.js → NotificationSettings-CDgYMjYK.js} +1 -1
- package/src/assets/web-panel/assets/{OrderTableRenderer-D1IAOQoo.js → OrderTableRenderer-BJkSlKeH.js} +1 -1
- package/src/assets/web-panel/assets/{Organization-9fqnPvTw.js → Organization-B-b3cWCp.js} +4 -4
- package/src/assets/web-panel/assets/{Overflow-n5xHkhow.js → Overflow-CILUL4nL.js} +1 -1
- package/src/assets/web-panel/assets/{P2P-B5AUWwH-.js → P2P-BTCFBkF0.js} +2 -2
- package/src/assets/web-panel/assets/{PdhVaultBrowser-CoUDV6dy.js → PdhVaultBrowser-BAR6AUx5.js} +4 -4
- package/src/assets/web-panel/assets/{Permissions-DPRmQf_H.js → Permissions-RRf6HbWJ.js} +4 -4
- package/src/assets/web-panel/assets/{PersonalDataHub-B37bsbEN.js → PersonalDataHub-BGTMiICa.js} +4 -4
- package/src/assets/web-panel/assets/{Pipeline-CCAuPqu9.js → Pipeline-945qY9YF.js} +1 -1
- package/src/assets/web-panel/assets/{Privacy-B81msZdv.js → Privacy-DPZwI6lJ.js} +1 -1
- package/src/assets/web-panel/assets/{ProjectInit-B-Py2NbF.js → ProjectInit-BPH2NRKc.js} +2 -2
- package/src/assets/web-panel/assets/{ProjectSettings-CYsneiRB.js → ProjectSettings-BZCRNCvb.js} +2 -2
- package/src/assets/web-panel/assets/{Projects-Y975Mh6R.js → Projects-BTI__tBt.js} +1 -1
- package/src/assets/web-panel/assets/{Providers-uO-10aP1.js → Providers-DDCf_nIw.js} +1 -1
- package/src/assets/web-panel/assets/{QrScannerModal-Bj_6gJ0l.js → QrScannerModal-N6rbqt1r.js} +1 -1
- package/src/assets/web-panel/assets/{QuickAsk-BgpBCMPE.js → QuickAsk-DrwjPS7M.js} +1 -1
- package/src/assets/web-panel/assets/{Recommend-DhUW3YUA.js → Recommend-Cog9Wd99.js} +1 -1
- package/src/assets/web-panel/assets/{RemoteSession-DdeyfbkA.js → RemoteSession-DC9pynjM.js} +2 -2
- package/src/assets/web-panel/assets/{Reputation-fxW421xd.js → Reputation-D4th4gKS.js} +1 -1
- package/src/assets/web-panel/assets/{Row-De73w1og.js → Row-ogiWR6v9.js} +1 -1
- package/src/assets/web-panel/assets/{RssFeed-3US_Q_lj.js → RssFeed-uYlk-2Oj.js} +3 -3
- package/src/assets/web-panel/assets/{Search-DbZ_DU74.js → Search-DYzzwvtM.js} +1 -1
- package/src/assets/web-panel/assets/{Security-DoIU1vMF.js → Security-Da7YOyX6.js} +4 -4
- package/src/assets/web-panel/assets/{Services-DB_RGqpc.js → Services-CLI1VM1X.js} +2 -2
- package/src/assets/web-panel/assets/{Skeleton-DnzLaayC.js → Skeleton-DK77dzDC.js} +1 -1
- package/src/assets/web-panel/assets/{Skills-VzVXHENF.js → Skills-C_z7Lmhl.js} +1 -1
- package/src/assets/web-panel/assets/{Sla-DMfvjCAI.js → Sla-D6413Q8L.js} +1 -1
- package/src/assets/web-panel/assets/{SpeechSettings-DQQOYWbF.js → SpeechSettings-C07ydbj2.js} +1 -1
- package/src/assets/web-panel/assets/{SyncSettings-Bmx3FG1P.js → SyncSettings-DGOm2W22.js} +2 -2
- package/src/assets/web-panel/assets/{Tasks-DOQyfb-2.js → Tasks-Cr_PVyLM.js} +1 -1
- package/src/assets/web-panel/assets/{Templates-D6hTRT7c.js → Templates-BhL2mZ0t.js} +1 -1
- package/src/assets/web-panel/assets/{Tenant-CTPQZAzs.js → Tenant-CPk5oyUr.js} +1 -1
- package/src/assets/web-panel/assets/{Terminal-B7vII3oH.js → Terminal-ClhqUsqV.js} +2 -2
- package/src/assets/web-panel/assets/{TimelineRenderer-BkiFJeXV.js → TimelineRenderer-BCC7MV5a.js} +1 -1
- package/src/assets/web-panel/assets/{Tokens-BPPdQMNo.js → Tokens-DD1Jt4oO.js} +1 -1
- package/src/assets/web-panel/assets/{Trigger-BxG8JAEI.js → Trigger-BCydFvN2.js} +1 -1
- package/src/assets/web-panel/assets/{Trust-DHD7nMpM.js → Trust-DDHT1noa.js} +1 -1
- package/src/assets/web-panel/assets/{UkeySign-B4BJpU47.js → UkeySign-CWPZC7oI.js} +1 -1
- package/src/assets/web-panel/assets/{VideoEditing-CRiGpFx2.js → VideoEditing-9Ea0nJR0.js} +1 -1
- package/src/assets/web-panel/assets/{Wallet-BSDSNiem.js → Wallet-BjxhLxTf.js} +4 -4
- package/src/assets/web-panel/assets/{WebAuthn-DLTUh5Zv.js → WebAuthn-C6uCye67.js} +5 -5
- package/src/assets/web-panel/assets/{WorkflowEditor-tW2MwjhD.js → WorkflowEditor-d2g4reYo.js} +1 -1
- package/src/assets/web-panel/assets/{chat-SgsLQ3FC.js → chat-frTc6ScX.js} +1 -1
- package/src/assets/web-panel/assets/{colors-BHjjLIKO.js → colors-B5K6lG3v.js} +1 -1
- package/src/assets/web-panel/assets/{compact-item-VceErdKq.js → compact-item-Cdn46r8D.js} +1 -1
- package/src/assets/web-panel/assets/{createContext-BZ332MYl.js → createContext-DU1jqVQi.js} +1 -1
- package/src/assets/web-panel/assets/devWarning-CP-jEUyw.js +1 -0
- package/src/assets/web-panel/assets/{hasIn-CBTjJOjn.js → hasIn-DdNH-aW_.js} +1 -1
- package/src/assets/web-panel/assets/{index-CawpdGTW.js → index-32ajXmTb.js} +1 -1
- package/src/assets/web-panel/assets/{index-BOtvPdHt.js → index-7ehytj1K.js} +1 -1
- package/src/assets/web-panel/assets/{index-vIXOpcjD.js → index-BC4eGkar.js} +1 -1
- package/src/assets/web-panel/assets/{index-Cr4rjlW5.js → index-BVLL-mq_.js} +1 -1
- package/src/assets/web-panel/assets/{index-D_SePDWj.js → index-BXKhhx9V.js} +1 -1
- package/src/assets/web-panel/assets/{index-BETEjuNr.js → index-BcGwGIaR.js} +1 -1
- package/src/assets/web-panel/assets/{index-BY1wexYG.js → index-Be4OtL-N.js} +1 -1
- package/src/assets/web-panel/assets/{index-BRWhLbWF.js → index-BoFak7le.js} +1 -1
- package/src/assets/web-panel/assets/{index-29wVzi-k.js → index-BwlIxAmX.js} +1 -1
- package/src/assets/web-panel/assets/{index-BeiqkxJG.js → index-CGiwfwB9.js} +1 -1
- package/src/assets/web-panel/assets/{index-BQSmZGgs.js → index-CHmkkdfT.js} +1 -1
- package/src/assets/web-panel/assets/{index-BhXZEUAc.js → index-CQorP1Ng.js} +1 -1
- package/src/assets/web-panel/assets/{index-C_B9rFQE.js → index-CUoNAmE8.js} +1 -1
- package/src/assets/web-panel/assets/{index-iYQNU9za.js → index-Cj6P9Nst.js} +1 -1
- package/src/assets/web-panel/assets/{index-DePOeMDM.js → index-CtRPi4YC.js} +1 -1
- package/src/assets/web-panel/assets/index-CzrCHrFy.js +1 -0
- package/src/assets/web-panel/assets/{index-DdefYBSw.js → index-D1qIC4GR.js} +1 -1
- package/src/assets/web-panel/assets/{index-By5jBtkV.js → index-D23xGYjM.js} +1 -1
- package/src/assets/web-panel/assets/{index-CMVvxIS8.js → index-D4l82Me1.js} +1 -1
- package/src/assets/web-panel/assets/{index-CLc19alH.js → index-D4miNkWv.js} +1 -1
- package/src/assets/web-panel/assets/{index-B2u26AS3.js → index-DIVyTmZ3.js} +1 -1
- package/src/assets/web-panel/assets/{index-DaCCy6lq.js → index-DM5FmPZU.js} +1 -1
- package/src/assets/web-panel/assets/{index-DUkVN794.js → index-DRzBBdQ0.js} +1 -1
- package/src/assets/web-panel/assets/{index-BPXFLYdW.js → index-Dca_i7H8.js} +1 -1
- package/src/assets/web-panel/assets/{index-CpumS98o.js → index-DftZuyA7.js} +1 -1
- package/src/assets/web-panel/assets/{index-CrUgDQkf.js → index-DtBCCkC4.js} +1 -1
- package/src/assets/web-panel/assets/{index-CtjLcCNU.js → index-HjCvXHf5.js} +1 -1
- package/src/assets/web-panel/assets/{index-CaIS_TNX.js → index-LaivuXyF.js} +1 -1
- package/src/assets/web-panel/assets/{index-BC1xuQgX.js → index-MM4Zz2OE.js} +1 -1
- package/src/assets/web-panel/assets/{index-BcUenmCX.js → index-NmEXVjzt.js} +1 -1
- package/src/assets/web-panel/assets/index-OJsGskZw.js +1 -0
- package/src/assets/web-panel/assets/{index-_PHrIO4O.js → index-PjAS0sIZ.js} +1 -1
- package/src/assets/web-panel/assets/{index-DgPvuIn2.js → index-VDM2KKo9.js} +1 -1
- package/src/assets/web-panel/assets/{index-B5KAGxNo.js → index-cYaWV4Mq.js} +1 -1
- package/src/assets/web-panel/assets/{index-B40dQn0G.js → index-gg3QPC3i.js} +3 -3
- package/src/assets/web-panel/assets/{index-BTlc-Bve.js → index-kVO_MDm-.js} +1 -1
- package/src/assets/web-panel/assets/{index-FJ3zC9LK.js → index-mgU3Q-lN.js} +1 -1
- package/src/assets/web-panel/assets/{index-1n7xjKDU.js → index-t_OBzIP4.js} +1 -1
- package/src/assets/web-panel/assets/{index-fU7wNHDB.js → index-yZRuZpwK.js} +1 -1
- package/src/assets/web-panel/assets/{initDefaultProps-D1oZNz0D.js → initDefaultProps-DwLTRNdo.js} +1 -1
- package/src/assets/web-panel/assets/{motion-C8k6doMO.js → motion-BchZDXnh.js} +1 -1
- package/src/assets/web-panel/assets/{move-DJ4qSbYt.js → move-e8M-zZFl.js} +1 -1
- package/src/assets/web-panel/assets/{mtc-parser-CdpJD85j.js → mtc-parser-Ci40fQb3.js} +1 -1
- package/src/assets/web-panel/assets/{omit-B0V4slKA.js → omit-C7110_f2.js} +1 -1
- package/src/assets/web-panel/assets/{pickAttrs-Di5NiW1F.js → pickAttrs-skteMgnM.js} +1 -1
- package/src/assets/web-panel/assets/{placementArrow-B5OMHgEM.js → placementArrow-MGZ-lh2B.js} +1 -1
- package/src/assets/web-panel/assets/{responsiveObserve-C6U9IWql.js → responsiveObserve-N4Sd7uy-.js} +1 -1
- package/src/assets/web-panel/assets/{slide-BMyTH3rN.js → slide-C7HDGRaa.js} +1 -1
- package/src/assets/web-panel/assets/{statusUtils-C7d6BG7Z.js → statusUtils-BhRexlgy.js} +1 -1
- package/src/assets/web-panel/assets/{styleChecker-CR50QnQy.js → styleChecker-ChEoQkN2.js} +1 -1
- package/src/assets/web-panel/assets/{useFlexGapSupport-DBEeoMb8.js → useFlexGapSupport-CDbG1s2Y.js} +1 -1
- package/src/assets/web-panel/assets/{useFs-3r099cjv.js → useFs-CUIx0tcJ.js} +1 -1
- package/src/assets/web-panel/assets/{usePersonalDataHub-C5IYNdTK.js → usePersonalDataHub-bkO7L7ec.js} +1 -1
- package/src/assets/web-panel/assets/{vnode-DmEOLRHf.js → vnode-B_ZvABIG.js} +1 -1
- package/src/assets/web-panel/assets/{zoom-DnpUiVCB.js → zoom-D5shjpS8.js} +1 -1
- package/src/assets/web-panel/index.html +1 -1
- package/src/command-help-index.json +4 -4
- package/src/commands/agent.js +3 -3
- package/src/commands/background-session.js +145 -0
- package/src/commands/mcp.js +125 -7
- package/src/commands/serve.js +1 -1
- package/src/commands/session-show.js +131 -0
- package/src/commands/session.js +50 -106
- package/src/commands/skill.js +87 -50
- package/src/data/changelog.json +14 -1
- package/src/gateways/ws/ws-agent-handler.js +8 -1
- package/src/harness/jsonl-session-store.js +428 -25
- package/src/harness/mcp-client.js +2652 -404
- package/src/harness/session-list-index.js +65 -7
- package/src/lazy-dispatch.js +76 -8
- package/src/lib/abort-utils.js +68 -0
- package/src/lib/agent-core.js +1 -59
- package/src/lib/chat-core.js +24 -6
- package/src/lib/cowork-mcp-tools.js +47 -2
- package/src/lib/cowork-task-runner.js +11 -3
- package/src/lib/doctor-checkup.js +9 -7
- package/src/lib/learning/skill-improver.js +21 -2
- package/src/lib/mcp-call-ledger-store.js +116 -3
- package/src/lib/mcp-call-ledger.js +59 -14
- package/src/lib/mcp-client.js +1 -0
- package/src/lib/mcp-stdio-executable-identity.js +598 -0
- package/src/lib/mcp-stdio-execution-authority.js +363 -0
- package/src/lib/mcp-tool-metadata.js +436 -0
- package/src/lib/mcp-tool-result.js +367 -0
- package/src/lib/observability/index.js +71 -0
- package/src/lib/pr-link-ledger.js +8 -27
- package/src/lib/pr-link-store.js +26 -0
- package/src/lib/process-execution-broker/index.js +14 -0
- package/src/lib/process-tree-termination.js +253 -0
- package/src/lib/session-host-snapshot.js +9 -5
- package/src/lib/session-projection.js +5 -1
- package/src/lib/session-tail.js +31 -1
- package/src/lib/skill-budget.js +222 -0
- package/src/lib/skill-execution-authority.js +381 -0
- package/src/lib/skill-execution-identity.js +39 -2
- package/src/lib/skill-loader.js +322 -86
- package/src/lib/skill-mcp.js +43 -1
- package/src/repl/agent-repl.js +174 -38
- package/src/repl/bg-dashboard.js +81 -28
- package/src/repl/chat-repl.js +77 -12
- package/src/runtime/agent-core.js +340 -108
- package/src/runtime/agent-runtime.js +26 -3
- package/src/runtime/headless-runner.js +150 -23
- package/src/runtime/headless-stream.js +309 -59
- package/src/runtime/mcp-config.js +157 -10
- package/src/runtime/mcp-tool-search.js +0 -0
- package/src/runtime/output-backpressure.js +366 -0
- package/src/runtime/pipe-safety.js +56 -21
- package/src/assets/web-panel/assets/ChatBubbleRenderer-BUUyJ5QO.js +0 -1
- package/src/assets/web-panel/assets/MarkdownRenderer-1mNozZ8v.js +0 -1
- package/src/assets/web-panel/assets/MobileProjects-DqnoFPgR.js +0 -1
- package/src/assets/web-panel/assets/devWarning-DosMaL-k.js +0 -1
- package/src/assets/web-panel/assets/index-0O5ePnUq.js +0 -1
- package/src/assets/web-panel/assets/index-ChAzPigF.js +0 -1
- package/src/lib/skill-process-broker.js +0 -85
package/src/repl/agent-repl.js
CHANGED
|
@@ -25,6 +25,7 @@ import path from "path";
|
|
|
25
25
|
import { randomUUID } from "node:crypto";
|
|
26
26
|
import { isPromise, isProxy } from "node:util/types";
|
|
27
27
|
import { logger } from "../lib/logger.js";
|
|
28
|
+
import { issueMcpStdioExecutionAuthority } from "../lib/mcp-stdio-execution-authority.js";
|
|
28
29
|
import { getPlanModeManager, PlanState } from "../lib/plan-mode.js";
|
|
29
30
|
import { createVimState, feedNormalKey } from "../lib/repl-vim.js";
|
|
30
31
|
import {
|
|
@@ -126,6 +127,7 @@ import { expandFileRefsAsync } from "../runtime/file-ref-expander.js";
|
|
|
126
127
|
import { prepareVisionTurn, resolveVisionLlm } from "../lib/image-input.js";
|
|
127
128
|
import { composeSystemPrompt } from "../runtime/system-prompt.js";
|
|
128
129
|
import { installPipeSafety } from "../runtime/pipe-safety.js";
|
|
130
|
+
import { installOutputBackpressure } from "../runtime/output-backpressure.js";
|
|
129
131
|
import {
|
|
130
132
|
makeFallbackChatFn,
|
|
131
133
|
normalizeFallbackModels,
|
|
@@ -323,6 +325,9 @@ async function executeTool(name, args) {
|
|
|
323
325
|
* unit-testable via the `options._coreLoop` injection seam.
|
|
324
326
|
*/
|
|
325
327
|
export async function agentLoop(messages, options) {
|
|
328
|
+
const writeOut =
|
|
329
|
+
options.writeOut || ((text) => process.stdout.write(String(text)));
|
|
330
|
+
const waitForOutput = options.waitForOutput;
|
|
326
331
|
// Resume-degenerate role merge (Claude Code 2.1.187 parity), gated by the
|
|
327
332
|
// one-shot `mergeRoles` flag so it fires only on the first model call after
|
|
328
333
|
// resuming a session whose prior run produced no assistant response. Collapse
|
|
@@ -343,7 +348,7 @@ export async function agentLoop(messages, options) {
|
|
|
343
348
|
const onProviderFallback =
|
|
344
349
|
options.onProviderFallback ||
|
|
345
350
|
((info) =>
|
|
346
|
-
|
|
351
|
+
writeOut(
|
|
347
352
|
chalk.yellow(
|
|
348
353
|
`\n ⚠️ ${info.message || `已从 "${info.from}" 切换到 "${info.to}"`}\n`,
|
|
349
354
|
),
|
|
@@ -380,7 +385,7 @@ export async function agentLoop(messages, options) {
|
|
|
380
385
|
event.type === "managed-checkpoint-error" || event.coverage === "none"
|
|
381
386
|
? chalk.yellow
|
|
382
387
|
: chalk.gray;
|
|
383
|
-
|
|
388
|
+
writeOut(render(`${managedCheckpointLine}\n`));
|
|
384
389
|
} else if (event.type === "checkpoint") {
|
|
385
390
|
// Remember which file snapshot lines up with the live conversation so
|
|
386
391
|
// `/rewind <n>` can restore code + conversation together (Claude-Code
|
|
@@ -393,7 +398,7 @@ export async function agentLoop(messages, options) {
|
|
|
393
398
|
tool: event.tool,
|
|
394
399
|
});
|
|
395
400
|
}
|
|
396
|
-
|
|
401
|
+
writeOut(
|
|
397
402
|
chalk.gray(` ⎌ checkpoint ${event.id} (before ${event.tool})\n`),
|
|
398
403
|
);
|
|
399
404
|
} else if (event.type === "tool-executing") {
|
|
@@ -402,7 +407,7 @@ export async function agentLoop(messages, options) {
|
|
|
402
407
|
args: event.args,
|
|
403
408
|
startedAt: options.now ? options.now() : Date.now(),
|
|
404
409
|
};
|
|
405
|
-
|
|
410
|
+
writeOut(
|
|
406
411
|
chalk.gray(
|
|
407
412
|
` [${event.tool}] ${formatToolArgs(event.tool, event.args)}\n`,
|
|
408
413
|
),
|
|
@@ -438,9 +443,7 @@ export async function agentLoop(messages, options) {
|
|
|
438
443
|
}
|
|
439
444
|
}
|
|
440
445
|
if (event.error || event.result?.error) {
|
|
441
|
-
|
|
442
|
-
chalk.red(` Error: ${event.error || event.result?.error}\n`),
|
|
443
|
-
);
|
|
446
|
+
writeOut(chalk.red(` Error: ${event.error || event.result?.error}\n`));
|
|
444
447
|
// Record policy denials (not plain tool failures) into the caller's
|
|
445
448
|
// denial log for review via `/permissions denials` (Claude-Code 2.1.193
|
|
446
449
|
// recent denials). Caller passes the session log (mirrors checkpointMarks)
|
|
@@ -477,13 +480,13 @@ export async function agentLoop(messages, options) {
|
|
|
477
480
|
const sid = options?.sessionId;
|
|
478
481
|
const policy = approval.policy || "strict";
|
|
479
482
|
if (sid && policy === "strict") {
|
|
480
|
-
|
|
483
|
+
writeOut(
|
|
481
484
|
chalk.yellow(
|
|
482
485
|
` Hint: relax policy with cc session policy ${sid} --set trusted\n`,
|
|
483
486
|
),
|
|
484
487
|
);
|
|
485
488
|
} else if (sid) {
|
|
486
|
-
|
|
489
|
+
writeOut(
|
|
487
490
|
chalk.yellow(
|
|
488
491
|
` Hint: per-session policy is "${policy}" — see cc session policy ${sid}\n`,
|
|
489
492
|
),
|
|
@@ -491,26 +494,26 @@ export async function agentLoop(messages, options) {
|
|
|
491
494
|
}
|
|
492
495
|
}
|
|
493
496
|
} else if (event.result?.success) {
|
|
494
|
-
|
|
497
|
+
writeOut(chalk.green(` Done\n`));
|
|
495
498
|
}
|
|
496
499
|
} else if (event.type === "thinking") {
|
|
497
500
|
// Intermediate-step reasoning (before a tool call) — dimmed, inline.
|
|
498
501
|
if (process.env.CC_REPL_THINKING !== "0" && event.text) {
|
|
499
|
-
|
|
502
|
+
writeOut(
|
|
500
503
|
"\n" + chalk.dim("💭 " + event.text.replace(/\n/g, "\n ")) + "\n",
|
|
501
504
|
);
|
|
502
505
|
}
|
|
503
506
|
} else if (event.type === "token-usage") {
|
|
504
507
|
usageEvents.push(event);
|
|
505
508
|
} else if (event.type === "iteration-warning") {
|
|
506
|
-
|
|
509
|
+
writeOut(chalk.yellow(`\n ${event.message}\n`));
|
|
507
510
|
} else if (event.type === "iteration-budget-exhausted") {
|
|
508
|
-
|
|
509
|
-
chalk.red(`\n [Budget Exhausted] ${event.budget}\n`),
|
|
510
|
-
);
|
|
511
|
+
writeOut(chalk.red(`\n [Budget Exhausted] ${event.budget}\n`));
|
|
511
512
|
} else if (event.type === "response-complete") {
|
|
513
|
+
await waitForOutput?.();
|
|
512
514
|
return { content: event.content, usageEvents, thinking: event.thinking };
|
|
513
515
|
}
|
|
516
|
+
await waitForOutput?.();
|
|
514
517
|
}
|
|
515
518
|
return { content: "", usageEvents };
|
|
516
519
|
}
|
|
@@ -1934,7 +1937,12 @@ export function createReplMcpHostRuntimeManager(dependencies = {}) {
|
|
|
1934
1937
|
}
|
|
1935
1938
|
|
|
1936
1939
|
const sink = persistLedger
|
|
1937
|
-
? createLedgerSink(
|
|
1940
|
+
? createLedgerSink(
|
|
1941
|
+
sessionId,
|
|
1942
|
+
appendLedgerEvent === appendAuthorityEvent
|
|
1943
|
+
? { recovery }
|
|
1944
|
+
: { appendEvent: appendLedgerEvent },
|
|
1945
|
+
)
|
|
1938
1946
|
: null;
|
|
1939
1947
|
const sharedController =
|
|
1940
1948
|
!verifiedRecovery && current?.sessionId === sessionId
|
|
@@ -2068,27 +2076,93 @@ export async function startAgentRepl(options = {}) {
|
|
|
2068
2076
|
}
|
|
2069
2077
|
|
|
2070
2078
|
async function startAgentReplInWorkspace(options = {}, startupAdmission) {
|
|
2079
|
+
let failureHandler = null;
|
|
2080
|
+
let disposePipeSafety = null;
|
|
2081
|
+
const flow = installOutputBackpressure({
|
|
2082
|
+
stdout: process.stdout,
|
|
2083
|
+
stderr: process.stderr,
|
|
2084
|
+
maxQueuedBytes: options.outputBackpressureMaxBytes,
|
|
2085
|
+
onFailure: (error) => failureHandler?.(error),
|
|
2086
|
+
});
|
|
2087
|
+
let restored = false;
|
|
2088
|
+
const outputScope = {
|
|
2089
|
+
flow,
|
|
2090
|
+
setFailureHandler(handler) {
|
|
2091
|
+
failureHandler = handler;
|
|
2092
|
+
},
|
|
2093
|
+
setPipeDisposer(disposer) {
|
|
2094
|
+
disposePipeSafety = disposer;
|
|
2095
|
+
},
|
|
2096
|
+
restore() {
|
|
2097
|
+
if (restored) return;
|
|
2098
|
+
restored = true;
|
|
2099
|
+
disposePipeSafety?.();
|
|
2100
|
+
flow.restore();
|
|
2101
|
+
},
|
|
2102
|
+
};
|
|
2103
|
+
try {
|
|
2104
|
+
return await startAgentReplInWorkspaceOwned(
|
|
2105
|
+
options,
|
|
2106
|
+
startupAdmission,
|
|
2107
|
+
outputScope,
|
|
2108
|
+
);
|
|
2109
|
+
} catch (error) {
|
|
2110
|
+
outputScope.restore();
|
|
2111
|
+
throw error;
|
|
2112
|
+
}
|
|
2113
|
+
}
|
|
2114
|
+
|
|
2115
|
+
async function startAgentReplInWorkspaceOwned(
|
|
2116
|
+
options = {},
|
|
2117
|
+
startupAdmission,
|
|
2118
|
+
outputScope,
|
|
2119
|
+
) {
|
|
2071
2120
|
// EPIPE guard: if the REPL's stdout is piped and the consumer closes (e.g.
|
|
2072
2121
|
// `cc agent | head`), the async stream `error` would otherwise crash the
|
|
2073
2122
|
// process. Route a broken pipe into the REPL's own graceful shutdown (the
|
|
2074
2123
|
// rl "close" handler — MCP disconnect, kill background tasks) when the
|
|
2075
|
-
// interface
|
|
2076
|
-
// cleanup write that also EPIPEs can't loop.
|
|
2124
|
+
// interface and its close handler exist. `_replClosing` makes it fire once so
|
|
2125
|
+
// a cleanup write that also EPIPEs can't loop. An early EPIPE remains pending
|
|
2126
|
+
// until the close handler is attached; it never skips directly to exit.
|
|
2077
2127
|
let _replRl = null;
|
|
2078
2128
|
let _replClosing = false;
|
|
2079
|
-
|
|
2129
|
+
let _replCloseReady = false;
|
|
2130
|
+
let _replCleanupStarted = false;
|
|
2131
|
+
let _turnAbort = null;
|
|
2132
|
+
let _replOutputFailure = null;
|
|
2133
|
+
const _requestReplOutputClose = (error) => {
|
|
2134
|
+
if (error && !_replOutputFailure) _replOutputFailure = error;
|
|
2135
|
+
if (error) {
|
|
2136
|
+
process.exitCode = error.code === "EPIPE" ? 0 : 1;
|
|
2137
|
+
_turnAbort?.abort(error);
|
|
2138
|
+
}
|
|
2080
2139
|
if (_replClosing) return;
|
|
2081
2140
|
_replClosing = true;
|
|
2082
|
-
if (_replRl) {
|
|
2141
|
+
if (_replRl && _replCloseReady) {
|
|
2083
2142
|
try {
|
|
2084
|
-
_replRl.close(); // triggers the graceful "close" cleanup
|
|
2085
|
-
return;
|
|
2143
|
+
_replRl.close(); // triggers the graceful "close" cleanup
|
|
2086
2144
|
} catch {
|
|
2087
|
-
|
|
2145
|
+
// The close handler owns shutdown. Leaving exitCode set allows the
|
|
2146
|
+
// event loop to drain without bypassing the teardown lifecycle.
|
|
2088
2147
|
}
|
|
2089
2148
|
}
|
|
2090
|
-
|
|
2091
|
-
|
|
2149
|
+
};
|
|
2150
|
+
const _replOutputFlow = outputScope.flow;
|
|
2151
|
+
outputScope.setFailureHandler(_requestReplOutputClose);
|
|
2152
|
+
const _disposeReplPipeSafety = installPipeSafety(undefined, () =>
|
|
2153
|
+
_requestReplOutputClose(
|
|
2154
|
+
Object.assign(new Error("REPL output pipe closed"), { code: "EPIPE" }),
|
|
2155
|
+
),
|
|
2156
|
+
);
|
|
2157
|
+
outputScope.setPipeDisposer(_disposeReplPipeSafety);
|
|
2158
|
+
const _waitForReplOutput = async () => {
|
|
2159
|
+
try {
|
|
2160
|
+
await _replOutputFlow.wait();
|
|
2161
|
+
return true;
|
|
2162
|
+
} catch {
|
|
2163
|
+
return false;
|
|
2164
|
+
}
|
|
2165
|
+
};
|
|
2092
2166
|
const { useJsonl, candidate: _startupJsonlResume } = startupAdmission;
|
|
2093
2167
|
let model = options.model || "qwen2.5:7b";
|
|
2094
2168
|
let provider = options.provider || "ollama";
|
|
@@ -2988,7 +3062,15 @@ async function startAgentReplInWorkspace(options = {}, startupAdmission) {
|
|
|
2988
3062
|
let connected = 0;
|
|
2989
3063
|
for (const [name, cfg] of serverEntries) {
|
|
2990
3064
|
try {
|
|
2991
|
-
|
|
3065
|
+
const authorizedConfig = { ...cfg };
|
|
3066
|
+
authorizedConfig.mcpStdioExecutionAuthority =
|
|
3067
|
+
issueMcpStdioExecutionAuthority({
|
|
3068
|
+
serverName: name,
|
|
3069
|
+
config: authorizedConfig,
|
|
3070
|
+
approvalKind: "explicit-config",
|
|
3071
|
+
approvalSource: `agent-bundle:${path.resolve(options.bundlePath)}`,
|
|
3072
|
+
});
|
|
3073
|
+
await _bundleMcpClient.connect(name, authorizedConfig);
|
|
2992
3074
|
connected += 1;
|
|
2993
3075
|
} catch (mcpErr) {
|
|
2994
3076
|
logger.log(
|
|
@@ -3868,7 +3950,7 @@ async function startAgentReplInWorkspace(options = {}, startupAdmission) {
|
|
|
3868
3950
|
// seam (throwIfAborted at each iteration); partial conversation is kept.
|
|
3869
3951
|
// Idle Esc presses (no active turn) are ignored, and escape-prefixed key
|
|
3870
3952
|
// sequences (arrows etc.) never reach here as bare "escape".
|
|
3871
|
-
|
|
3953
|
+
_turnAbort = null;
|
|
3872
3954
|
let _lastIdleEscAt = 0;
|
|
3873
3955
|
let _replKeypressHandler = null;
|
|
3874
3956
|
if (process.stdin.isTTY) {
|
|
@@ -4089,7 +4171,9 @@ async function startAgentReplInWorkspace(options = {}, startupAdmission) {
|
|
|
4089
4171
|
});
|
|
4090
4172
|
// Custom command wins; otherwise the built-in context-usage render.
|
|
4091
4173
|
if (_slCfg) {
|
|
4092
|
-
return _sl.renderStatusLine(_slCfg, context, {
|
|
4174
|
+
return _sl.renderStatusLine(_slCfg, context, {
|
|
4175
|
+
cwd: process.cwd(),
|
|
4176
|
+
});
|
|
4093
4177
|
}
|
|
4094
4178
|
const line = _sl.renderDefaultStatusLine(context);
|
|
4095
4179
|
return line && line.trim() ? line : null;
|
|
@@ -4423,7 +4507,7 @@ async function startAgentReplInWorkspace(options = {}, startupAdmission) {
|
|
|
4423
4507
|
` ${chalk.cyan("/add-dir")} Add an extra working root (/add-dir <dir>; no arg lists roots)`,
|
|
4424
4508
|
);
|
|
4425
4509
|
logger.log(
|
|
4426
|
-
` ${chalk.cyan("/reload-skills")}
|
|
4510
|
+
` ${chalk.cyan("/reload-skills")} Revoke process Skill grants and re-scan layers`,
|
|
4427
4511
|
);
|
|
4428
4512
|
logger.log(
|
|
4429
4513
|
` ${chalk.cyan("/reload-plugins")} Re-scan installed plugins after add/trust/upgrade`,
|
|
@@ -5283,7 +5367,11 @@ async function startAgentReplInWorkspace(options = {}, startupAdmission) {
|
|
|
5283
5367
|
dispatchAsyncHooks(
|
|
5284
5368
|
_settingsHooks,
|
|
5285
5369
|
"CwdChanged",
|
|
5286
|
-
{
|
|
5370
|
+
{
|
|
5371
|
+
old_cwd: oldCwd,
|
|
5372
|
+
cwd: newCwd,
|
|
5373
|
+
session_id: sessionId || null,
|
|
5374
|
+
},
|
|
5287
5375
|
{ cwd: newCwd, supervisor: _asyncHookSupervisor },
|
|
5288
5376
|
);
|
|
5289
5377
|
} catch {
|
|
@@ -5847,6 +5935,11 @@ async function startAgentReplInWorkspace(options = {}, startupAdmission) {
|
|
|
5847
5935
|
// layers (incl. .claude/skills) without restarting the session.
|
|
5848
5936
|
if (trimmed === "/reload-skills") {
|
|
5849
5937
|
try {
|
|
5938
|
+
_replSkillLoader.revokeExecutionAuthorizations?.({
|
|
5939
|
+
message:
|
|
5940
|
+
"Skill execution authorization was revoked by /reload-skills",
|
|
5941
|
+
reasonCode: "reload-skills",
|
|
5942
|
+
});
|
|
5850
5943
|
_replSkillLoader.clearCache();
|
|
5851
5944
|
const n = _replSkillLoader.loadAll().length;
|
|
5852
5945
|
_replBaseSystem = _buildReplBaseSystem();
|
|
@@ -5856,7 +5949,7 @@ async function startAgentReplInWorkspace(options = {}, startupAdmission) {
|
|
|
5856
5949
|
_persistReplContextSources();
|
|
5857
5950
|
logger.log(
|
|
5858
5951
|
chalk.green(
|
|
5859
|
-
`✔ skills reloaded — ${n} available (${_replSkillLoader.getLayerPaths().length} layers re-scanned)`,
|
|
5952
|
+
`✔ Skill grants revoked and skills reloaded — ${n} available (${_replSkillLoader.getLayerPaths().length} layers re-scanned)`,
|
|
5860
5953
|
),
|
|
5861
5954
|
);
|
|
5862
5955
|
} catch (err) {
|
|
@@ -6069,7 +6162,9 @@ async function startAgentReplInWorkspace(options = {}, startupAdmission) {
|
|
|
6069
6162
|
logger.log(chalk.gray(l));
|
|
6070
6163
|
} else {
|
|
6071
6164
|
// set — createReplGoal throws on a malformed spec (caught below).
|
|
6072
|
-
_sessionGoal = rg.createReplGoal(cmd.spec, {
|
|
6165
|
+
_sessionGoal = rg.createReplGoal(cmd.spec, {
|
|
6166
|
+
now: () => Date.now(),
|
|
6167
|
+
});
|
|
6073
6168
|
for (const l of rg.renderGoalStart(_sessionGoal))
|
|
6074
6169
|
logger.log(chalk.cyan(l));
|
|
6075
6170
|
}
|
|
@@ -7679,22 +7774,29 @@ async function startAgentReplInWorkspace(options = {}, startupAdmission) {
|
|
|
7679
7774
|
// reasoning dimmed before it. Skipped (left undefined) in replay mode.
|
|
7680
7775
|
let _liveStreamed = false;
|
|
7681
7776
|
let _liveThinkStarted = false;
|
|
7777
|
+
const _settleLiveWrite = (blocked) =>
|
|
7778
|
+
blocked ? _replOutputFlow.wait() : undefined;
|
|
7682
7779
|
const liveOpts = _streamLive
|
|
7683
7780
|
? {
|
|
7684
7781
|
onToken: (t) => {
|
|
7685
7782
|
// Separate the answer from the dimmed reasoning above it (once).
|
|
7686
|
-
|
|
7687
|
-
|
|
7783
|
+
let blocked = false;
|
|
7784
|
+
if (!_liveStreamed && _liveThinkStarted) {
|
|
7785
|
+
blocked = process.stdout.write("\n") === false;
|
|
7786
|
+
}
|
|
7688
7787
|
_liveStreamed = true;
|
|
7689
|
-
process.stdout.write(t);
|
|
7788
|
+
blocked = process.stdout.write(t) === false || blocked;
|
|
7789
|
+
return _settleLiveWrite(blocked);
|
|
7690
7790
|
},
|
|
7691
7791
|
onThinking: (t) => {
|
|
7692
7792
|
if (process.env.CC_REPL_THINKING === "0") return;
|
|
7793
|
+
let blocked = false;
|
|
7693
7794
|
if (!_liveThinkStarted) {
|
|
7694
7795
|
process.stdout.write(chalk.dim("💭 "));
|
|
7695
7796
|
_liveThinkStarted = true;
|
|
7696
7797
|
}
|
|
7697
|
-
process.stdout.write(chalk.dim(t));
|
|
7798
|
+
blocked = process.stdout.write(chalk.dim(t)) === false || blocked;
|
|
7799
|
+
return _settleLiveWrite(blocked);
|
|
7698
7800
|
},
|
|
7699
7801
|
}
|
|
7700
7802
|
: {};
|
|
@@ -7726,7 +7828,9 @@ async function startAgentReplInWorkspace(options = {}, startupAdmission) {
|
|
|
7726
7828
|
if (_settingsHooks && !_asyncHookSupervisor) {
|
|
7727
7829
|
const { AsyncHookSupervisor } =
|
|
7728
7830
|
await import("../lib/async-hook-supervisor.js");
|
|
7729
|
-
_asyncHookSupervisor = new AsyncHookSupervisor({
|
|
7831
|
+
_asyncHookSupervisor = new AsyncHookSupervisor({
|
|
7832
|
+
persistStats: true,
|
|
7833
|
+
});
|
|
7730
7834
|
}
|
|
7731
7835
|
const activeMcpRuntime = _activateMcpHostRuntime();
|
|
7732
7836
|
const activeRawMcpClient = activeMcpRuntime.rawClient || undefined;
|
|
@@ -7736,6 +7840,7 @@ async function startAgentReplInWorkspace(options = {}, startupAdmission) {
|
|
|
7736
7840
|
thinking: reasoning,
|
|
7737
7841
|
} = await agentLoop(messages, {
|
|
7738
7842
|
...liveOpts,
|
|
7843
|
+
waitForOutput: _replOutputFlow.wait,
|
|
7739
7844
|
mergeRoles: _mergeRolesThisTurn,
|
|
7740
7845
|
// Visible auto-retry feedback (Claude-Code 2.1.181): when the model's
|
|
7741
7846
|
// streaming call hits a transient connection drop and retries, tell the
|
|
@@ -8170,6 +8275,7 @@ async function startAgentReplInWorkspace(options = {}, startupAdmission) {
|
|
|
8170
8275
|
};
|
|
8171
8276
|
|
|
8172
8277
|
rl.on("line", async (input) => {
|
|
8278
|
+
if (!(await _waitForReplOutput())) return;
|
|
8173
8279
|
if (_processingLine) {
|
|
8174
8280
|
const concurrentBtw = _slashCommandsDisabled
|
|
8175
8281
|
? null
|
|
@@ -8193,10 +8299,12 @@ async function startAgentReplInWorkspace(options = {}, startupAdmission) {
|
|
|
8193
8299
|
_processingLine = true;
|
|
8194
8300
|
try {
|
|
8195
8301
|
await handleLine(input);
|
|
8302
|
+
if (!(await _waitForReplOutput())) return;
|
|
8196
8303
|
while (_pendingLines.length) {
|
|
8197
8304
|
const next = _pendingLines.shift();
|
|
8198
8305
|
logger.log(chalk.cyan(`▶ running queued input: ${next}`));
|
|
8199
8306
|
await handleLine(next);
|
|
8307
|
+
if (!(await _waitForReplOutput())) return;
|
|
8200
8308
|
}
|
|
8201
8309
|
} finally {
|
|
8202
8310
|
_processingLine = false;
|
|
@@ -8204,6 +8312,9 @@ async function startAgentReplInWorkspace(options = {}, startupAdmission) {
|
|
|
8204
8312
|
});
|
|
8205
8313
|
|
|
8206
8314
|
rl.on("close", async () => {
|
|
8315
|
+
if (_replCleanupStarted) return;
|
|
8316
|
+
_replCleanupStarted = true;
|
|
8317
|
+
_replClosing = true;
|
|
8207
8318
|
_promptInteractions.dispose();
|
|
8208
8319
|
if (process.stdin.isTTY) {
|
|
8209
8320
|
if (_replKeypressHandler) {
|
|
@@ -8391,6 +8502,31 @@ async function startAgentReplInWorkspace(options = {}, startupAdmission) {
|
|
|
8391
8502
|
} catch (_e) {
|
|
8392
8503
|
// Non-critical
|
|
8393
8504
|
}
|
|
8394
|
-
|
|
8505
|
+
if (!_replOutputFailure) {
|
|
8506
|
+
try {
|
|
8507
|
+
await _replOutputFlow.wait();
|
|
8508
|
+
} catch (error) {
|
|
8509
|
+
_replOutputFailure ||= error;
|
|
8510
|
+
}
|
|
8511
|
+
}
|
|
8512
|
+
const outputExitCode = _replOutputFailure
|
|
8513
|
+
? _replOutputFailure.code === "EPIPE"
|
|
8514
|
+
? 0
|
|
8515
|
+
: 1
|
|
8516
|
+
: 0;
|
|
8517
|
+
outputScope.restore();
|
|
8518
|
+
process.exit(outputExitCode);
|
|
8395
8519
|
});
|
|
8520
|
+
_replCloseReady = true;
|
|
8521
|
+
if (_replClosing) {
|
|
8522
|
+
// stdout may have broken before readline and its cleanup handler were fully
|
|
8523
|
+
// initialized. Close only now so the complete async teardown runs.
|
|
8524
|
+
queueMicrotask(() => {
|
|
8525
|
+
try {
|
|
8526
|
+
rl.close();
|
|
8527
|
+
} catch {
|
|
8528
|
+
process.exitCode = 0;
|
|
8529
|
+
}
|
|
8530
|
+
});
|
|
8531
|
+
}
|
|
8396
8532
|
}
|
package/src/repl/bg-dashboard.js
CHANGED
|
@@ -20,6 +20,8 @@
|
|
|
20
20
|
|
|
21
21
|
import chalk from "chalk";
|
|
22
22
|
import { phaseGroupKey } from "../lib/background-agent-phase.js";
|
|
23
|
+
import { installPipeSafety } from "../runtime/pipe-safety.js";
|
|
24
|
+
import { installOutputBackpressure } from "../runtime/output-backpressure.js";
|
|
23
25
|
|
|
24
26
|
export const GROUP_ORDER = [
|
|
25
27
|
"needs-input",
|
|
@@ -171,31 +173,52 @@ const ALT_EXIT = `${ESC}[?25h${ESC}[?1049l`;
|
|
|
171
173
|
const CLEAR = `${ESC}[2J${ESC}[H`;
|
|
172
174
|
|
|
173
175
|
/** Default terminal IO — swapped for a fake in tests. */
|
|
174
|
-
export function defaultDashboardIo(
|
|
176
|
+
export function defaultDashboardIo({
|
|
177
|
+
input = process.stdin,
|
|
178
|
+
stdout = process.stdout,
|
|
179
|
+
stderr = process.stderr,
|
|
180
|
+
} = {}) {
|
|
181
|
+
let failure = null;
|
|
182
|
+
const recordFailure = (error) => {
|
|
183
|
+
failure ||= error;
|
|
184
|
+
process.exitCode = error?.code === "EPIPE" ? 0 : 1;
|
|
185
|
+
};
|
|
186
|
+
const outputFlow = installOutputBackpressure({
|
|
187
|
+
stdout,
|
|
188
|
+
stderr,
|
|
189
|
+
onFailure: recordFailure,
|
|
190
|
+
});
|
|
191
|
+
const disposePipeSafety = installPipeSafety([stdout, stderr], () =>
|
|
192
|
+
recordFailure(
|
|
193
|
+
Object.assign(new Error("dashboard output pipe closed"), {
|
|
194
|
+
code: "EPIPE",
|
|
195
|
+
}),
|
|
196
|
+
),
|
|
197
|
+
);
|
|
175
198
|
return {
|
|
176
|
-
isTTY: Boolean(
|
|
177
|
-
write: (s) =>
|
|
178
|
-
enterAlt: () =>
|
|
179
|
-
exitAlt: () =>
|
|
180
|
-
clear: () =>
|
|
199
|
+
isTTY: Boolean(stdout.isTTY && input.isTTY),
|
|
200
|
+
write: (s) => stdout.write(s),
|
|
201
|
+
enterAlt: () => stdout.write(ALT_ENTER),
|
|
202
|
+
exitAlt: () => stdout.write(ALT_EXIT),
|
|
203
|
+
clear: () => stdout.write(CLEAR),
|
|
181
204
|
setRaw: (on) => {
|
|
182
|
-
if (
|
|
183
|
-
|
|
205
|
+
if (input.isTTY && input.setRawMode) {
|
|
206
|
+
input.setRawMode(on);
|
|
184
207
|
}
|
|
185
208
|
},
|
|
186
209
|
async listenKeys(onKey) {
|
|
187
210
|
const readline = await import("node:readline");
|
|
188
|
-
readline.emitKeypressEvents(
|
|
189
|
-
|
|
211
|
+
readline.emitKeypressEvents(input);
|
|
212
|
+
input.resume();
|
|
190
213
|
const handler = (str, key) => onKey(str, key || {});
|
|
191
|
-
|
|
192
|
-
return () =>
|
|
214
|
+
input.on("keypress", handler);
|
|
215
|
+
return () => input.off("keypress", handler);
|
|
193
216
|
},
|
|
194
217
|
async promptLine(question) {
|
|
195
218
|
const readline = await import("node:readline");
|
|
196
219
|
const rl = readline.createInterface({
|
|
197
|
-
input
|
|
198
|
-
output:
|
|
220
|
+
input,
|
|
221
|
+
output: stdout,
|
|
199
222
|
});
|
|
200
223
|
try {
|
|
201
224
|
return await new Promise((resolve) => rl.question(question, resolve));
|
|
@@ -203,6 +226,15 @@ export function defaultDashboardIo() {
|
|
|
203
226
|
rl.close();
|
|
204
227
|
}
|
|
205
228
|
},
|
|
229
|
+
async wait() {
|
|
230
|
+
if (failure) throw failure;
|
|
231
|
+
await outputFlow.wait();
|
|
232
|
+
if (failure) throw failure;
|
|
233
|
+
},
|
|
234
|
+
dispose() {
|
|
235
|
+
disposePipeSafety();
|
|
236
|
+
outputFlow.restore();
|
|
237
|
+
},
|
|
206
238
|
};
|
|
207
239
|
}
|
|
208
240
|
|
|
@@ -224,23 +256,36 @@ export async function runBgDashboard(deps = {}) {
|
|
|
224
256
|
let filterIndex = 0;
|
|
225
257
|
let peek = null;
|
|
226
258
|
let message = "";
|
|
259
|
+
let outputFailed = false;
|
|
260
|
+
let finishDashboard = () => {};
|
|
261
|
+
let renderChain = Promise.resolve();
|
|
227
262
|
let model = buildDashboardModel(listAgents(), {
|
|
228
263
|
filter: FILTERS[filterIndex],
|
|
229
264
|
now: now(),
|
|
230
265
|
});
|
|
231
266
|
|
|
232
267
|
const render = () => {
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
268
|
+
renderChain = renderChain
|
|
269
|
+
.then(async () => {
|
|
270
|
+
model = buildDashboardModel(listAgents(), {
|
|
271
|
+
filter: FILTERS[filterIndex],
|
|
272
|
+
now: now(),
|
|
273
|
+
});
|
|
274
|
+
if (selected >= model.flat.length)
|
|
275
|
+
selected = Math.max(0, model.flat.length - 1);
|
|
276
|
+
if (peek && !model.flat.some((s) => s.id === peek.id)) peek = null;
|
|
277
|
+
io.clear();
|
|
278
|
+
io.write(
|
|
279
|
+
renderDashboard(model, { selectedIndex: selected, peek, message }) +
|
|
280
|
+
"\n",
|
|
281
|
+
);
|
|
282
|
+
await io.wait?.();
|
|
283
|
+
})
|
|
284
|
+
.catch(() => {
|
|
285
|
+
outputFailed = true;
|
|
286
|
+
finishDashboard();
|
|
287
|
+
});
|
|
288
|
+
return renderChain;
|
|
244
289
|
};
|
|
245
290
|
|
|
246
291
|
const selectedSession = () => model.flat[selected] || null;
|
|
@@ -259,13 +304,15 @@ export async function runBgDashboard(deps = {}) {
|
|
|
259
304
|
|
|
260
305
|
io.enterAlt();
|
|
261
306
|
io.setRaw(true);
|
|
262
|
-
const timer = setInterval(render, refreshMs);
|
|
307
|
+
const timer = setInterval(() => void render(), refreshMs);
|
|
263
308
|
timer.unref?.();
|
|
264
|
-
render();
|
|
309
|
+
await render();
|
|
265
310
|
|
|
266
311
|
let stopListening = () => {};
|
|
267
312
|
await new Promise((resolve) => {
|
|
268
313
|
const finish = () => resolve();
|
|
314
|
+
finishDashboard = finish;
|
|
315
|
+
if (outputFailed) finish();
|
|
269
316
|
const onKey = async (str, key) => {
|
|
270
317
|
const name = key?.name || str;
|
|
271
318
|
const ctrlC = key?.ctrl && key.name === "c";
|
|
@@ -342,7 +389,7 @@ export async function runBgDashboard(deps = {}) {
|
|
|
342
389
|
} catch (err) {
|
|
343
390
|
message = `error: ${err.message}`;
|
|
344
391
|
}
|
|
345
|
-
render();
|
|
392
|
+
await render();
|
|
346
393
|
};
|
|
347
394
|
io.listenKeys(onKey).then((stop) => {
|
|
348
395
|
stopListening = stop || (() => {});
|
|
@@ -353,4 +400,10 @@ export async function runBgDashboard(deps = {}) {
|
|
|
353
400
|
stopListening();
|
|
354
401
|
io.setRaw(false);
|
|
355
402
|
io.exitAlt();
|
|
403
|
+
try {
|
|
404
|
+
await io.wait?.();
|
|
405
|
+
} catch {
|
|
406
|
+
// The output owner already classified the exit code.
|
|
407
|
+
}
|
|
408
|
+
io.dispose?.();
|
|
356
409
|
}
|