chainlesschain 0.162.193 → 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/README.md +29 -6
- package/completions/_cc +2 -2
- package/completions/cc.bash +2 -2
- package/completions/cc.fish +2 -2
- package/completions/cc.ps1 +2 -2
- package/package.json +1 -1
- package/src/assets/web-panel/assets/{AIOps-DBLkaTje.js → AIOps-CSN-D2xb.js} +1 -1
- package/src/assets/web-panel/assets/{ActionButton-BAy-Qebo.js → ActionButton-CBSMlGzv.js} +1 -1
- package/src/assets/web-panel/assets/{Analytics-Dfb1Mlau.js → Analytics-3yr3Nt2t.js} +3 -3
- package/src/assets/web-panel/assets/{AppLayout-BbFkN8m0.js → AppLayout-1b-o3lMH.js} +4 -4
- package/src/assets/web-panel/assets/{Artifacts-kM4-cEEH.js → Artifacts-Dr8rJi17.js} +1 -1
- package/src/assets/web-panel/assets/{Audit-BSQfYWf3.js → Audit-2V689HC2.js} +1 -1
- package/src/assets/web-panel/assets/{BackgroundAgents-CWDbI_fG.js → BackgroundAgents-DIxkNrD_.js} +1 -1
- package/src/assets/web-panel/assets/{Backup-BxLPbq_Q.js → Backup-Pg6BcwrE.js} +1 -1
- package/src/assets/web-panel/assets/{BaseInput-DuIbdrp6.js → BaseInput-CLUpqyfH.js} +1 -1
- package/src/assets/web-panel/assets/{Chat-NEmjGahU.js → Chat-qVS8rD4G.js} +5 -5
- package/src/assets/web-panel/assets/{ChatBubbleRenderer-DfJEo-5_.js → ChatBubbleRenderer-ti49X12Q.js} +1 -1
- package/src/assets/web-panel/assets/{Checkbox-CaoeG3Ns.js → Checkbox-Bs9AaEay.js} +1 -1
- package/src/assets/web-panel/assets/{Codegen-BFf_g3xY.js → Codegen-Dvc1mq8q.js} +1 -1
- package/src/assets/web-panel/assets/{Col-CNOd8mdQ.js → Col-C4_biO7A.js} +1 -1
- package/src/assets/web-panel/assets/{Community-BhpDqz-i.js → Community-al25XQUV.js} +1 -1
- package/src/assets/web-panel/assets/{Compact-CQ42T7Z6.js → Compact-D-ZDB8M1.js} +1 -1
- package/src/assets/web-panel/assets/{Compliance-DI5PcLMN.js → Compliance-6lIfDz1W.js} +1 -1
- package/src/assets/web-panel/assets/{Cowork-wYYZe0wR.js → Cowork-DdI48XMJ.js} +3 -3
- package/src/assets/web-panel/assets/{Cron-Dlq03DD_.js → Cron-DF5HZd2g.js} +2 -2
- package/src/assets/web-panel/assets/{Crosschain-BzqS8PP3.js → Crosschain-DuxeSvt3.js} +1 -1
- package/src/assets/web-panel/assets/{DID-It55Yem7.js → DID-B9yHkUTd.js} +2 -2
- package/src/assets/web-panel/assets/{Dashboard-9HkH6oxn.js → Dashboard-Z5EuI3IM.js} +2 -2
- package/src/assets/web-panel/assets/{Dropdown-00e2YWRB.js → Dropdown-vuRfO3dY.js} +1 -1
- package/src/assets/web-panel/assets/{EmailListRenderer-By-ugRDL.js → EmailListRenderer-QFLCc1uL.js} +1 -1
- package/src/assets/web-panel/assets/{FamilyGuardDashboard-C396yu5m.js → FamilyGuardDashboard-BZ7EXkFJ.js} +1 -1
- package/src/assets/web-panel/assets/{Federation-BME5sbZY.js → Federation-DycIEg2s.js} +1 -1
- package/src/assets/web-panel/assets/{FormItemContext-B4TEZCr-.js → FormItemContext-Ba0H_7yG.js} +1 -1
- package/src/assets/web-panel/assets/{GenericCardRenderer-_9CX9ieY.js → GenericCardRenderer--JgSTK-C.js} +1 -1
- package/src/assets/web-panel/assets/{Git-BrS6WVSr.js → Git-DEvTbldE.js} +2 -2
- package/src/assets/web-panel/assets/{Governance-BMdCWnKF.js → Governance-BJIljy18.js} +1 -1
- package/src/assets/web-panel/assets/{Inference-Bcp3vzcA.js → Inference-CVyW3G3q.js} +1 -1
- package/src/assets/web-panel/assets/{KnowledgeGraph-DlLuqGY5.js → KnowledgeGraph-v8-borge.js} +1 -1
- package/src/assets/web-panel/assets/{Logs-D-W_6x09.js → Logs-BCdS7gGo.js} +2 -2
- package/src/assets/web-panel/assets/{MarkdownRenderer-DElGPxCZ.js → MarkdownRenderer-CU-JB2Ns.js} +1 -1
- package/src/assets/web-panel/assets/{Marketplace-Dzn2guvI.js → Marketplace-AN6tGA2B.js} +1 -1
- package/src/assets/web-panel/assets/{McpTools-uPict-KL.js → McpTools-DGViXASN.js} +6 -6
- package/src/assets/web-panel/assets/{Memory-BCw43kOb.js → Memory-Bpvmx9X4.js} +2 -2
- package/src/assets/web-panel/assets/{MobileBridge-DmV8Wv7F.js → MobileBridge-CTP-DOTC.js} +1 -1
- package/src/assets/web-panel/assets/{MobileProjects-C-HaCmSM.js → MobileProjects-C8qfktxn.js} +1 -1
- package/src/assets/web-panel/assets/{Mtc-C6jxJ34Y.js → Mtc-ndYr0M1s.js} +5 -5
- package/src/assets/web-panel/assets/{MtcAudit-Za8ExwhQ.js → MtcAudit-Ci0-YK7W.js} +5 -5
- package/src/assets/web-panel/assets/{Multisig-KKDbuEgb.js → Multisig-BehSTFW7.js} +3 -3
- package/src/assets/web-panel/assets/{NLProgramming-Dj97zbCk.js → NLProgramming-B91_YBLv.js} +1 -1
- package/src/assets/web-panel/assets/{Notes-DBXvMraU.js → Notes-BQlJ0-gq.js} +4 -4
- package/src/assets/web-panel/assets/{NotificationSettings-DOxITLYl.js → NotificationSettings-CDgYMjYK.js} +1 -1
- package/src/assets/web-panel/assets/{OrderTableRenderer-7DVjZwMM.js → OrderTableRenderer-BJkSlKeH.js} +1 -1
- package/src/assets/web-panel/assets/{Organization-D1i73mjb.js → Organization-B-b3cWCp.js} +4 -4
- package/src/assets/web-panel/assets/{Overflow-kW6Q3rvg.js → Overflow-CILUL4nL.js} +1 -1
- package/src/assets/web-panel/assets/{P2P-C-v_8p2v.js → P2P-BTCFBkF0.js} +2 -2
- package/src/assets/web-panel/assets/{PdhVaultBrowser-tUbbY64W.js → PdhVaultBrowser-BAR6AUx5.js} +5 -5
- package/src/assets/web-panel/assets/{Permissions-DkHxEZ58.js → Permissions-RRf6HbWJ.js} +4 -4
- package/src/assets/web-panel/assets/{PersonalDataHub-DWuMPGrK.js → PersonalDataHub-BGTMiICa.js} +4 -4
- package/src/assets/web-panel/assets/{Pipeline-30IYXWWm.js → Pipeline-945qY9YF.js} +1 -1
- package/src/assets/web-panel/assets/{Privacy-DZNQ4RrH.js → Privacy-DPZwI6lJ.js} +1 -1
- package/src/assets/web-panel/assets/{ProjectInit-De2MCElf.js → ProjectInit-BPH2NRKc.js} +2 -2
- package/src/assets/web-panel/assets/{ProjectSettings-5uHk9wsC.js → ProjectSettings-BZCRNCvb.js} +2 -2
- package/src/assets/web-panel/assets/{Projects-XjDfrFLb.js → Projects-BTI__tBt.js} +1 -1
- package/src/assets/web-panel/assets/{Providers-Bgwj6gcE.js → Providers-DDCf_nIw.js} +1 -1
- package/src/assets/web-panel/assets/{QrScannerModal-gKql9umv.js → QrScannerModal-N6rbqt1r.js} +1 -1
- package/src/assets/web-panel/assets/{QuickAsk-BWvmwkh7.js → QuickAsk-DrwjPS7M.js} +1 -1
- package/src/assets/web-panel/assets/{Recommend-Uok4hZPd.js → Recommend-Cog9Wd99.js} +1 -1
- package/src/assets/web-panel/assets/{RemoteSession-DLJzTk9a.js → RemoteSession-DC9pynjM.js} +3 -3
- package/src/assets/web-panel/assets/{Reputation-ZVwIPMSO.js → Reputation-D4th4gKS.js} +1 -1
- package/src/assets/web-panel/assets/{Row-CN04HxWR.js → Row-ogiWR6v9.js} +1 -1
- package/src/assets/web-panel/assets/{RssFeed-BdSrEG4g.js → RssFeed-uYlk-2Oj.js} +3 -3
- package/src/assets/web-panel/assets/{Search-5PwVd0IR.js → Search-DYzzwvtM.js} +1 -1
- package/src/assets/web-panel/assets/{Security-DdBo4zjN.js → Security-Da7YOyX6.js} +4 -4
- package/src/assets/web-panel/assets/{Services-D3letU7M.js → Services-CLI1VM1X.js} +2 -2
- package/src/assets/web-panel/assets/{Skeleton--Jb83f1v.js → Skeleton-DK77dzDC.js} +1 -1
- package/src/assets/web-panel/assets/{Skills-Bg-CulHX.js → Skills-C_z7Lmhl.js} +1 -1
- package/src/assets/web-panel/assets/{Sla-7QOa7S-X.js → Sla-D6413Q8L.js} +1 -1
- package/src/assets/web-panel/assets/{SpeechSettings-DQwppjRw.js → SpeechSettings-C07ydbj2.js} +1 -1
- package/src/assets/web-panel/assets/{SyncSettings-dNPQGL-s.js → SyncSettings-DGOm2W22.js} +2 -2
- package/src/assets/web-panel/assets/{Tasks-Ggs-Z48q.js → Tasks-Cr_PVyLM.js} +1 -1
- package/src/assets/web-panel/assets/{Templates-78TYHgRO.js → Templates-BhL2mZ0t.js} +1 -1
- package/src/assets/web-panel/assets/{Tenant-xHwwv9vO.js → Tenant-CPk5oyUr.js} +1 -1
- package/src/assets/web-panel/assets/{Terminal-BJXSJfm4.js → Terminal-ClhqUsqV.js} +2 -2
- package/src/assets/web-panel/assets/{TimelineRenderer-fLbjRhJw.js → TimelineRenderer-BCC7MV5a.js} +1 -1
- package/src/assets/web-panel/assets/{Tokens-oMSBSBmB.js → Tokens-DD1Jt4oO.js} +1 -1
- package/src/assets/web-panel/assets/{Trigger-CfnY_wX_.js → Trigger-BCydFvN2.js} +1 -1
- package/src/assets/web-panel/assets/{Trust-AdyTgYKO.js → Trust-DDHT1noa.js} +1 -1
- package/src/assets/web-panel/assets/{UkeySign-C5K_81dS.js → UkeySign-CWPZC7oI.js} +1 -1
- package/src/assets/web-panel/assets/{VideoEditing-vcGVDqki.js → VideoEditing-9Ea0nJR0.js} +1 -1
- package/src/assets/web-panel/assets/{Wallet-hhOc3hlY.js → Wallet-BjxhLxTf.js} +4 -4
- package/src/assets/web-panel/assets/{WebAuthn-CVloC11s.js → WebAuthn-C6uCye67.js} +4 -4
- package/src/assets/web-panel/assets/{WorkflowEditor-CYeE32R5.js → WorkflowEditor-d2g4reYo.js} +1 -1
- package/src/assets/web-panel/assets/{chat-CNROR5bN.js → chat-frTc6ScX.js} +1 -1
- package/src/assets/web-panel/assets/{colors-Cjx-cnwU.js → colors-B5K6lG3v.js} +1 -1
- package/src/assets/web-panel/assets/{compact-item-DOIfelvD.js → compact-item-Cdn46r8D.js} +1 -1
- package/src/assets/web-panel/assets/{createContext-DfA7UZ8u.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-BMSCzcfM.js → hasIn-DdNH-aW_.js} +1 -1
- package/src/assets/web-panel/assets/{index-CAnvhwg6.js → index-32ajXmTb.js} +1 -1
- package/src/assets/web-panel/assets/{index-BEMwBBHk.js → index-7ehytj1K.js} +1 -1
- package/src/assets/web-panel/assets/{index-CVL27kXC.js → index-BC4eGkar.js} +1 -1
- package/src/assets/web-panel/assets/{index-FBc_CJBd.js → index-BVLL-mq_.js} +1 -1
- package/src/assets/web-panel/assets/{index-dCd8cSUq.js → index-BXKhhx9V.js} +1 -1
- package/src/assets/web-panel/assets/{index-BemJ2u_E.js → index-BcGwGIaR.js} +1 -1
- package/src/assets/web-panel/assets/{index-CVm1_i3t.js → index-Be4OtL-N.js} +1 -1
- package/src/assets/web-panel/assets/{index-Cs6l7uRu.js → index-BoFak7le.js} +1 -1
- package/src/assets/web-panel/assets/{index-DPAWBrMf.js → index-BwlIxAmX.js} +1 -1
- package/src/assets/web-panel/assets/{index-DLmn0BoK.js → index-CGiwfwB9.js} +1 -1
- package/src/assets/web-panel/assets/{index-B7gf77CJ.js → index-CHmkkdfT.js} +1 -1
- package/src/assets/web-panel/assets/{index-m2vG9gG_.js → index-CQorP1Ng.js} +1 -1
- package/src/assets/web-panel/assets/{index-ClNMrqEY.js → index-CUoNAmE8.js} +1 -1
- package/src/assets/web-panel/assets/{index-DHtpHZAQ.js → index-Cj6P9Nst.js} +1 -1
- package/src/assets/web-panel/assets/{index-C2K9SNVX.js → index-CtRPi4YC.js} +1 -1
- package/src/assets/web-panel/assets/index-CzrCHrFy.js +1 -0
- package/src/assets/web-panel/assets/{index-BolaOQxx.js → index-D1qIC4GR.js} +1 -1
- package/src/assets/web-panel/assets/{index-BM1y_9hM.js → index-D23xGYjM.js} +1 -1
- package/src/assets/web-panel/assets/{index-Cyweh0Hy.js → index-D4l82Me1.js} +1 -1
- package/src/assets/web-panel/assets/{index-By61ojUx.js → index-D4miNkWv.js} +1 -1
- package/src/assets/web-panel/assets/{index-BbQ2eQ8H.js → index-DIVyTmZ3.js} +1 -1
- package/src/assets/web-panel/assets/{index-Icdoqmzt.js → index-DM5FmPZU.js} +1 -1
- package/src/assets/web-panel/assets/{index-DjEsxO5h.js → index-DRzBBdQ0.js} +1 -1
- package/src/assets/web-panel/assets/{index-B4iYzpkL.js → index-Dca_i7H8.js} +1 -1
- package/src/assets/web-panel/assets/{index-C3L5gdiD.js → index-DftZuyA7.js} +1 -1
- package/src/assets/web-panel/assets/{index-CdYBye8B.js → index-DtBCCkC4.js} +1 -1
- package/src/assets/web-panel/assets/{index-AWg-0M1S.js → index-HjCvXHf5.js} +1 -1
- package/src/assets/web-panel/assets/{index-CEKMcOyU.js → index-LaivuXyF.js} +1 -1
- package/src/assets/web-panel/assets/{index-BxOk9vkG.js → index-MM4Zz2OE.js} +1 -1
- package/src/assets/web-panel/assets/{index-CNC5onm4.js → index-NmEXVjzt.js} +1 -1
- package/src/assets/web-panel/assets/index-OJsGskZw.js +1 -0
- package/src/assets/web-panel/assets/{index-BUonmKSE.js → index-PjAS0sIZ.js} +1 -1
- package/src/assets/web-panel/assets/{index-B5Udeb_6.js → index-VDM2KKo9.js} +1 -1
- package/src/assets/web-panel/assets/{index-5-HXgOX5.js → index-cYaWV4Mq.js} +1 -1
- package/src/assets/web-panel/assets/{index-DBpWXFed.js → index-gg3QPC3i.js} +3 -3
- package/src/assets/web-panel/assets/{index-C7xn0Nub.js → index-kVO_MDm-.js} +1 -1
- package/src/assets/web-panel/assets/{index-ti_lnTWl.js → index-mgU3Q-lN.js} +1 -1
- package/src/assets/web-panel/assets/{index-CshQbilz.js → index-t_OBzIP4.js} +1 -1
- package/src/assets/web-panel/assets/{index-mTpHoEzr.js → index-yZRuZpwK.js} +1 -1
- package/src/assets/web-panel/assets/{initDefaultProps-Bu8k7H1Y.js → initDefaultProps-DwLTRNdo.js} +1 -1
- package/src/assets/web-panel/assets/{motion-s3iVK41k.js → motion-BchZDXnh.js} +1 -1
- package/src/assets/web-panel/assets/{move-B6B-E5Jz.js → move-e8M-zZFl.js} +1 -1
- package/src/assets/web-panel/assets/{mtc-parser-BiYgTidA.js → mtc-parser-Ci40fQb3.js} +1 -1
- package/src/assets/web-panel/assets/{omit-pU1RdUDA.js → omit-C7110_f2.js} +1 -1
- package/src/assets/web-panel/assets/{pickAttrs-Cdoq_SeM.js → pickAttrs-skteMgnM.js} +1 -1
- package/src/assets/web-panel/assets/{placementArrow-DIuvAn2K.js → placementArrow-MGZ-lh2B.js} +1 -1
- package/src/assets/web-panel/assets/{responsiveObserve-8w16YR2D.js → responsiveObserve-N4Sd7uy-.js} +1 -1
- package/src/assets/web-panel/assets/{slide-VZ01cZe3.js → slide-C7HDGRaa.js} +1 -1
- package/src/assets/web-panel/assets/{statusUtils-lh8YMN8Z.js → statusUtils-BhRexlgy.js} +1 -1
- package/src/assets/web-panel/assets/{styleChecker-CmDyreec.js → styleChecker-ChEoQkN2.js} +1 -1
- package/src/assets/web-panel/assets/{useFlexGapSupport-hiJWMU5y.js → useFlexGapSupport-CDbG1s2Y.js} +1 -1
- package/src/assets/web-panel/assets/{useFs-Thnu-Iaj.js → useFs-CUIx0tcJ.js} +1 -1
- package/src/assets/web-panel/assets/{usePersonalDataHub-LBdOxjwY.js → usePersonalDataHub-bkO7L7ec.js} +1 -1
- package/src/assets/web-panel/assets/{vnode-Cg6lFY_X.js → vnode-B_ZvABIG.js} +1 -1
- package/src/assets/web-panel/assets/{zoom-DVfsR1YO.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/command-manifest.json +214 -50
- package/src/command-surface-policy.js +115 -0
- 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 +18 -5
- 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/public-ide-capability-manifest.js +6 -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 +349 -46
- package/src/repl/bg-dashboard.js +81 -28
- package/src/repl/chat-repl.js +77 -12
- package/src/repl/prompt-interactions.js +67 -7
- package/src/repl/slash-command-registry.js +3 -4
- 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/devWarning-ChcCBGs7.js +0 -1
- package/src/assets/web-panel/assets/index-Q2IajL54.js +0 -1
- package/src/assets/web-panel/assets/index-bRkHgOMc.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 {
|
|
@@ -123,9 +124,10 @@ import {
|
|
|
123
124
|
} from "../runtime/agent-core.js";
|
|
124
125
|
import { formatBackgroundTasks } from "./tasks-status.js";
|
|
125
126
|
import { expandFileRefsAsync } from "../runtime/file-ref-expander.js";
|
|
126
|
-
import { prepareVisionTurn } from "../lib/image-input.js";
|
|
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,
|
|
@@ -145,6 +147,10 @@ import {
|
|
|
145
147
|
} from "./btw-command.js";
|
|
146
148
|
import { shouldStreamLive } from "./stream-decision.js";
|
|
147
149
|
import { emptyTurnNotice } from "./empty-turn-notice.js";
|
|
150
|
+
import {
|
|
151
|
+
createPromptInteractionSurface,
|
|
152
|
+
mergeClipboardImageChips,
|
|
153
|
+
} from "./prompt-interactions.js";
|
|
148
154
|
import {
|
|
149
155
|
buildPermissionPrompt,
|
|
150
156
|
resolveAskIdleTimeoutMs,
|
|
@@ -319,6 +325,9 @@ async function executeTool(name, args) {
|
|
|
319
325
|
* unit-testable via the `options._coreLoop` injection seam.
|
|
320
326
|
*/
|
|
321
327
|
export async function agentLoop(messages, options) {
|
|
328
|
+
const writeOut =
|
|
329
|
+
options.writeOut || ((text) => process.stdout.write(String(text)));
|
|
330
|
+
const waitForOutput = options.waitForOutput;
|
|
322
331
|
// Resume-degenerate role merge (Claude Code 2.1.187 parity), gated by the
|
|
323
332
|
// one-shot `mergeRoles` flag so it fires only on the first model call after
|
|
324
333
|
// resuming a session whose prior run produced no assistant response. Collapse
|
|
@@ -339,7 +348,7 @@ export async function agentLoop(messages, options) {
|
|
|
339
348
|
const onProviderFallback =
|
|
340
349
|
options.onProviderFallback ||
|
|
341
350
|
((info) =>
|
|
342
|
-
|
|
351
|
+
writeOut(
|
|
343
352
|
chalk.yellow(
|
|
344
353
|
`\n ⚠️ ${info.message || `已从 "${info.from}" 切换到 "${info.to}"`}\n`,
|
|
345
354
|
),
|
|
@@ -376,7 +385,7 @@ export async function agentLoop(messages, options) {
|
|
|
376
385
|
event.type === "managed-checkpoint-error" || event.coverage === "none"
|
|
377
386
|
? chalk.yellow
|
|
378
387
|
: chalk.gray;
|
|
379
|
-
|
|
388
|
+
writeOut(render(`${managedCheckpointLine}\n`));
|
|
380
389
|
} else if (event.type === "checkpoint") {
|
|
381
390
|
// Remember which file snapshot lines up with the live conversation so
|
|
382
391
|
// `/rewind <n>` can restore code + conversation together (Claude-Code
|
|
@@ -389,7 +398,7 @@ export async function agentLoop(messages, options) {
|
|
|
389
398
|
tool: event.tool,
|
|
390
399
|
});
|
|
391
400
|
}
|
|
392
|
-
|
|
401
|
+
writeOut(
|
|
393
402
|
chalk.gray(` ⎌ checkpoint ${event.id} (before ${event.tool})\n`),
|
|
394
403
|
);
|
|
395
404
|
} else if (event.type === "tool-executing") {
|
|
@@ -398,7 +407,7 @@ export async function agentLoop(messages, options) {
|
|
|
398
407
|
args: event.args,
|
|
399
408
|
startedAt: options.now ? options.now() : Date.now(),
|
|
400
409
|
};
|
|
401
|
-
|
|
410
|
+
writeOut(
|
|
402
411
|
chalk.gray(
|
|
403
412
|
` [${event.tool}] ${formatToolArgs(event.tool, event.args)}\n`,
|
|
404
413
|
),
|
|
@@ -434,9 +443,7 @@ export async function agentLoop(messages, options) {
|
|
|
434
443
|
}
|
|
435
444
|
}
|
|
436
445
|
if (event.error || event.result?.error) {
|
|
437
|
-
|
|
438
|
-
chalk.red(` Error: ${event.error || event.result?.error}\n`),
|
|
439
|
-
);
|
|
446
|
+
writeOut(chalk.red(` Error: ${event.error || event.result?.error}\n`));
|
|
440
447
|
// Record policy denials (not plain tool failures) into the caller's
|
|
441
448
|
// denial log for review via `/permissions denials` (Claude-Code 2.1.193
|
|
442
449
|
// recent denials). Caller passes the session log (mirrors checkpointMarks)
|
|
@@ -473,13 +480,13 @@ export async function agentLoop(messages, options) {
|
|
|
473
480
|
const sid = options?.sessionId;
|
|
474
481
|
const policy = approval.policy || "strict";
|
|
475
482
|
if (sid && policy === "strict") {
|
|
476
|
-
|
|
483
|
+
writeOut(
|
|
477
484
|
chalk.yellow(
|
|
478
485
|
` Hint: relax policy with cc session policy ${sid} --set trusted\n`,
|
|
479
486
|
),
|
|
480
487
|
);
|
|
481
488
|
} else if (sid) {
|
|
482
|
-
|
|
489
|
+
writeOut(
|
|
483
490
|
chalk.yellow(
|
|
484
491
|
` Hint: per-session policy is "${policy}" — see cc session policy ${sid}\n`,
|
|
485
492
|
),
|
|
@@ -487,26 +494,26 @@ export async function agentLoop(messages, options) {
|
|
|
487
494
|
}
|
|
488
495
|
}
|
|
489
496
|
} else if (event.result?.success) {
|
|
490
|
-
|
|
497
|
+
writeOut(chalk.green(` Done\n`));
|
|
491
498
|
}
|
|
492
499
|
} else if (event.type === "thinking") {
|
|
493
500
|
// Intermediate-step reasoning (before a tool call) — dimmed, inline.
|
|
494
501
|
if (process.env.CC_REPL_THINKING !== "0" && event.text) {
|
|
495
|
-
|
|
502
|
+
writeOut(
|
|
496
503
|
"\n" + chalk.dim("💭 " + event.text.replace(/\n/g, "\n ")) + "\n",
|
|
497
504
|
);
|
|
498
505
|
}
|
|
499
506
|
} else if (event.type === "token-usage") {
|
|
500
507
|
usageEvents.push(event);
|
|
501
508
|
} else if (event.type === "iteration-warning") {
|
|
502
|
-
|
|
509
|
+
writeOut(chalk.yellow(`\n ${event.message}\n`));
|
|
503
510
|
} else if (event.type === "iteration-budget-exhausted") {
|
|
504
|
-
|
|
505
|
-
chalk.red(`\n [Budget Exhausted] ${event.budget}\n`),
|
|
506
|
-
);
|
|
511
|
+
writeOut(chalk.red(`\n [Budget Exhausted] ${event.budget}\n`));
|
|
507
512
|
} else if (event.type === "response-complete") {
|
|
513
|
+
await waitForOutput?.();
|
|
508
514
|
return { content: event.content, usageEvents, thinking: event.thinking };
|
|
509
515
|
}
|
|
516
|
+
await waitForOutput?.();
|
|
510
517
|
}
|
|
511
518
|
return { content: "", usageEvents };
|
|
512
519
|
}
|
|
@@ -1930,7 +1937,12 @@ export function createReplMcpHostRuntimeManager(dependencies = {}) {
|
|
|
1930
1937
|
}
|
|
1931
1938
|
|
|
1932
1939
|
const sink = persistLedger
|
|
1933
|
-
? createLedgerSink(
|
|
1940
|
+
? createLedgerSink(
|
|
1941
|
+
sessionId,
|
|
1942
|
+
appendLedgerEvent === appendAuthorityEvent
|
|
1943
|
+
? { recovery }
|
|
1944
|
+
: { appendEvent: appendLedgerEvent },
|
|
1945
|
+
)
|
|
1934
1946
|
: null;
|
|
1935
1947
|
const sharedController =
|
|
1936
1948
|
!verifiedRecovery && current?.sessionId === sessionId
|
|
@@ -2064,27 +2076,93 @@ export async function startAgentRepl(options = {}) {
|
|
|
2064
2076
|
}
|
|
2065
2077
|
|
|
2066
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
|
+
) {
|
|
2067
2120
|
// EPIPE guard: if the REPL's stdout is piped and the consumer closes (e.g.
|
|
2068
2121
|
// `cc agent | head`), the async stream `error` would otherwise crash the
|
|
2069
2122
|
// process. Route a broken pipe into the REPL's own graceful shutdown (the
|
|
2070
2123
|
// rl "close" handler — MCP disconnect, kill background tasks) when the
|
|
2071
|
-
// interface
|
|
2072
|
-
// 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.
|
|
2073
2127
|
let _replRl = null;
|
|
2074
2128
|
let _replClosing = false;
|
|
2075
|
-
|
|
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
|
+
}
|
|
2076
2139
|
if (_replClosing) return;
|
|
2077
2140
|
_replClosing = true;
|
|
2078
|
-
if (_replRl) {
|
|
2141
|
+
if (_replRl && _replCloseReady) {
|
|
2079
2142
|
try {
|
|
2080
|
-
_replRl.close(); // triggers the graceful "close" cleanup
|
|
2081
|
-
return;
|
|
2143
|
+
_replRl.close(); // triggers the graceful "close" cleanup
|
|
2082
2144
|
} catch {
|
|
2083
|
-
|
|
2145
|
+
// The close handler owns shutdown. Leaving exitCode set allows the
|
|
2146
|
+
// event loop to drain without bypassing the teardown lifecycle.
|
|
2084
2147
|
}
|
|
2085
2148
|
}
|
|
2086
|
-
|
|
2087
|
-
|
|
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
|
+
};
|
|
2088
2166
|
const { useJsonl, candidate: _startupJsonlResume } = startupAdmission;
|
|
2089
2167
|
let model = options.model || "qwen2.5:7b";
|
|
2090
2168
|
let provider = options.provider || "ollama";
|
|
@@ -2134,6 +2212,8 @@ async function startAgentReplInWorkspace(options = {}, startupAdmission) {
|
|
|
2134
2212
|
// an auto-detected image path so the REPL switches to a vision-capable model
|
|
2135
2213
|
// for that turn only (resolveVisionLlm falls back to the default when unset).
|
|
2136
2214
|
let _visionModel;
|
|
2215
|
+
let _promptInteractionConfig = {};
|
|
2216
|
+
let _persistPromptSuggestionsEnabled = null;
|
|
2137
2217
|
// Hard stream inactivity timeout override (config.llm.streamStallTimeoutMs,
|
|
2138
2218
|
// ms): a stream silent that long is aborted + retried instead of hanging.
|
|
2139
2219
|
// Unset → agent-core's 180s default (matches cc chat/ask). Set to 0 to
|
|
@@ -2148,8 +2228,11 @@ async function startAgentReplInWorkspace(options = {}, startupAdmission) {
|
|
|
2148
2228
|
// CC_PERMISSION_ASK_TIMEOUT_MS env > config permissions.askTimeoutMs > off.
|
|
2149
2229
|
let _askIdleTimeoutMs = resolveAskIdleTimeoutMs();
|
|
2150
2230
|
try {
|
|
2151
|
-
const
|
|
2152
|
-
const _cfg = loadConfig();
|
|
2231
|
+
const configManager = await import("../lib/config-manager.js");
|
|
2232
|
+
const _cfg = configManager.loadConfig();
|
|
2233
|
+
_promptInteractionConfig = _cfg || {};
|
|
2234
|
+
_persistPromptSuggestionsEnabled = (enabled) =>
|
|
2235
|
+
configManager.setConfigValue("cli.promptSuggestions", enabled === true);
|
|
2153
2236
|
_visionModel = _cfg?.llm?.visionModel || undefined;
|
|
2154
2237
|
_autoPinCfgValue = _cfg?.context?.autoPin;
|
|
2155
2238
|
_askIdleTimeoutMs = resolveAskIdleTimeoutMs({
|
|
@@ -2979,7 +3062,15 @@ async function startAgentReplInWorkspace(options = {}, startupAdmission) {
|
|
|
2979
3062
|
let connected = 0;
|
|
2980
3063
|
for (const [name, cfg] of serverEntries) {
|
|
2981
3064
|
try {
|
|
2982
|
-
|
|
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);
|
|
2983
3074
|
connected += 1;
|
|
2984
3075
|
} catch (mcpErr) {
|
|
2985
3076
|
logger.log(
|
|
@@ -3588,11 +3679,13 @@ async function startAgentReplInWorkspace(options = {}, startupAdmission) {
|
|
|
3588
3679
|
"/note-next",
|
|
3589
3680
|
"/output-style",
|
|
3590
3681
|
"/permissions",
|
|
3682
|
+
"/paste-image",
|
|
3591
3683
|
"/plan",
|
|
3592
3684
|
"/pr-comments",
|
|
3593
3685
|
"/profile",
|
|
3594
3686
|
"/provider",
|
|
3595
3687
|
"/quit",
|
|
3688
|
+
"/recap",
|
|
3596
3689
|
"/reindex",
|
|
3597
3690
|
"/release-notes",
|
|
3598
3691
|
"/reload-plugins",
|
|
@@ -3606,7 +3699,9 @@ async function startAgentReplInWorkspace(options = {}, startupAdmission) {
|
|
|
3606
3699
|
"/stats",
|
|
3607
3700
|
"/status",
|
|
3608
3701
|
"/statusline",
|
|
3702
|
+
"/stash",
|
|
3609
3703
|
"/sub-agents",
|
|
3704
|
+
"/suggestions",
|
|
3610
3705
|
"/task",
|
|
3611
3706
|
"/tasks",
|
|
3612
3707
|
"/terminal-setup",
|
|
@@ -3617,6 +3712,7 @@ async function startAgentReplInWorkspace(options = {}, startupAdmission) {
|
|
|
3617
3712
|
"/ultrathink",
|
|
3618
3713
|
"/vim",
|
|
3619
3714
|
"/voice",
|
|
3715
|
+
"/editor",
|
|
3620
3716
|
],
|
|
3621
3717
|
// User/project custom commands (.claude/commands/*.md) join TAB completion
|
|
3622
3718
|
// alongside the built-ins above. Sync + best-effort; the completer
|
|
@@ -3660,6 +3756,28 @@ async function startAgentReplInWorkspace(options = {}, startupAdmission) {
|
|
|
3660
3756
|
// "close" cleanup instead of a bare exit.
|
|
3661
3757
|
_replRl = rl;
|
|
3662
3758
|
|
|
3759
|
+
// Prompt-side interaction modules share the production readline surface.
|
|
3760
|
+
// A per-REPL registry avoids singleton handlers retaining an old session.
|
|
3761
|
+
const _promptInteractionSurface = createPromptInteractionSurface({
|
|
3762
|
+
readline: rl,
|
|
3763
|
+
config: _promptInteractionConfig,
|
|
3764
|
+
keybindings: _promptInteractionConfig,
|
|
3765
|
+
getSessionId: () => sessionId,
|
|
3766
|
+
getSuggestionContext: () => ({ messages: messages.slice() }),
|
|
3767
|
+
persistSuggestionEnabled: _persistPromptSuggestionsEnabled,
|
|
3768
|
+
clipboardBinding: options.clipboardBinding || null,
|
|
3769
|
+
generateSuggestions: options.generatePromptSuggestions,
|
|
3770
|
+
suggestionDebounceMs: options.promptSuggestionDebounceMs,
|
|
3771
|
+
screenReader: _screenReaderMode,
|
|
3772
|
+
write: (text) => process.stdout.write(String(text)),
|
|
3773
|
+
writeError: (text) => process.stderr.write(String(text)),
|
|
3774
|
+
getColumns: () => process.stdout.columns || 80,
|
|
3775
|
+
});
|
|
3776
|
+
const _promptInteractions = _promptInteractionSurface.controller;
|
|
3777
|
+
for (const diagnostic of _promptInteractions.diagnostics().keybindingErrors) {
|
|
3778
|
+
logger.warn(`prompt keybinding ignored: ${diagnostic}`);
|
|
3779
|
+
}
|
|
3780
|
+
|
|
3663
3781
|
// MCP elicitation in the interactive REPL uses the same readline surface as
|
|
3664
3782
|
// approvals, while MCPClient retains the protocol-level response shape.
|
|
3665
3783
|
const mcpElicitationClients = [_adhocMcp?.mcpClient, _bundleMcpClient].filter(
|
|
@@ -3757,9 +3875,34 @@ async function startAgentReplInWorkspace(options = {}, startupAdmission) {
|
|
|
3757
3875
|
// Vim-mode plumbing: capture readline's OWN keypress listeners now so we can
|
|
3758
3876
|
// suspend them while in NORMAL mode (the engine drives editing then) and
|
|
3759
3877
|
// reattach them for INSERT mode (readline's rich editing/history/completion).
|
|
3760
|
-
const
|
|
3878
|
+
const _nativeReadlineKeypressListeners = process.stdin.isTTY
|
|
3761
3879
|
? process.stdin.listeners("keypress").slice()
|
|
3762
3880
|
: [];
|
|
3881
|
+
const _consumedReadlineKeypresses = new WeakSet();
|
|
3882
|
+
const _consumeReadlineKeypress = (key) => {
|
|
3883
|
+
if (key && typeof key === "object") _consumedReadlineKeypresses.add(key);
|
|
3884
|
+
};
|
|
3885
|
+
const _rlKeypressListeners = _nativeReadlineKeypressListeners.map(
|
|
3886
|
+
(listener) =>
|
|
3887
|
+
function guardedReadlineKeypress(input, key) {
|
|
3888
|
+
if (
|
|
3889
|
+
key &&
|
|
3890
|
+
typeof key === "object" &&
|
|
3891
|
+
_consumedReadlineKeypresses.has(key)
|
|
3892
|
+
) {
|
|
3893
|
+
return;
|
|
3894
|
+
}
|
|
3895
|
+
return listener.call(this, input, key);
|
|
3896
|
+
},
|
|
3897
|
+
);
|
|
3898
|
+
if (process.stdin.isTTY) {
|
|
3899
|
+
for (const listener of _nativeReadlineKeypressListeners) {
|
|
3900
|
+
process.stdin.removeListener("keypress", listener);
|
|
3901
|
+
}
|
|
3902
|
+
for (const listener of _rlKeypressListeners) {
|
|
3903
|
+
process.stdin.on("keypress", listener);
|
|
3904
|
+
}
|
|
3905
|
+
}
|
|
3763
3906
|
const _suspendReadlineKeys = () => {
|
|
3764
3907
|
for (const l of _rlKeypressListeners)
|
|
3765
3908
|
process.stdin.removeListener("keypress", l);
|
|
@@ -3807,13 +3950,15 @@ async function startAgentReplInWorkspace(options = {}, startupAdmission) {
|
|
|
3807
3950
|
// seam (throwIfAborted at each iteration); partial conversation is kept.
|
|
3808
3951
|
// Idle Esc presses (no active turn) are ignored, and escape-prefixed key
|
|
3809
3952
|
// sequences (arrows etc.) never reach here as bare "escape".
|
|
3810
|
-
|
|
3953
|
+
_turnAbort = null;
|
|
3811
3954
|
let _lastIdleEscAt = 0;
|
|
3955
|
+
let _replKeypressHandler = null;
|
|
3812
3956
|
if (process.stdin.isTTY) {
|
|
3813
|
-
|
|
3957
|
+
_replKeypressHandler = (_str, key) => {
|
|
3814
3958
|
const k = key || {};
|
|
3815
3959
|
// 1) Turn abort always wins, regardless of vim mode.
|
|
3816
3960
|
if (k.name === "escape" && !k.meta && _turnAbort) {
|
|
3961
|
+
_consumeReadlineKeypress(key);
|
|
3817
3962
|
process.stdout.write(chalk.yellow("\n⎋ interrupting…\n"));
|
|
3818
3963
|
try {
|
|
3819
3964
|
_turnAbort.abort();
|
|
@@ -3849,6 +3994,7 @@ async function startAgentReplInWorkspace(options = {}, startupAdmission) {
|
|
|
3849
3994
|
const isShiftTab =
|
|
3850
3995
|
(k.name === "tab" && k.shift) || k.sequence === "\u001b[Z";
|
|
3851
3996
|
if (isShiftTab) {
|
|
3997
|
+
_consumeReadlineKeypress(key);
|
|
3852
3998
|
if (
|
|
3853
3999
|
_approvalGate &&
|
|
3854
4000
|
sessionId &&
|
|
@@ -3877,14 +4023,30 @@ async function startAgentReplInWorkspace(options = {}, startupAdmission) {
|
|
|
3877
4023
|
return;
|
|
3878
4024
|
}
|
|
3879
4025
|
|
|
4026
|
+
// 1.75) Prompt interaction keybindings share the controller used by
|
|
4027
|
+
// slash commands. They are idle-only and stay out of Vim NORMAL mode,
|
|
4028
|
+
// where the modal editor owns every key.
|
|
4029
|
+
if (
|
|
4030
|
+
!_turnAbort &&
|
|
4031
|
+
(!_vimEnabled || !_vim) &&
|
|
4032
|
+
_promptInteractions.handleKeypress(_str, k)
|
|
4033
|
+
) {
|
|
4034
|
+
_consumeReadlineKeypress(key);
|
|
4035
|
+
return;
|
|
4036
|
+
}
|
|
4037
|
+
|
|
3880
4038
|
// 2) Vim mode: modal editing on the current input line.
|
|
3881
4039
|
if (_vimEnabled && !_turnAbort) {
|
|
3882
4040
|
if (!_vim) {
|
|
3883
4041
|
// INSERT mode — readline owns the keys; Esc switches to NORMAL.
|
|
3884
|
-
if (k.name === "escape" && !k.meta)
|
|
4042
|
+
if (k.name === "escape" && !k.meta) {
|
|
4043
|
+
_consumeReadlineKeypress(key);
|
|
4044
|
+
_vimEnterNormal();
|
|
4045
|
+
}
|
|
3885
4046
|
return;
|
|
3886
4047
|
}
|
|
3887
4048
|
// NORMAL mode — readline suspended; the engine interprets every key.
|
|
4049
|
+
_consumeReadlineKeypress(key);
|
|
3888
4050
|
const res = feedNormalKey(_vim, _str || "", k);
|
|
3889
4051
|
if (res.submit) {
|
|
3890
4052
|
// Hand the line to readline as a normal Enter (fires 'line', clears).
|
|
@@ -3906,6 +4068,7 @@ async function startAgentReplInWorkspace(options = {}, startupAdmission) {
|
|
|
3906
4068
|
|
|
3907
4069
|
// 3) Non-vim: double-Esc while idle → rewind picker shortcut.
|
|
3908
4070
|
if (k.name !== "escape" || k.meta) return;
|
|
4071
|
+
_consumeReadlineKeypress(key);
|
|
3909
4072
|
const nowTs = Date.now();
|
|
3910
4073
|
if (nowTs - _lastIdleEscAt < 600) {
|
|
3911
4074
|
_lastIdleEscAt = 0;
|
|
@@ -3930,7 +4093,8 @@ async function startAgentReplInWorkspace(options = {}, startupAdmission) {
|
|
|
3930
4093
|
} else {
|
|
3931
4094
|
_lastIdleEscAt = nowTs;
|
|
3932
4095
|
}
|
|
3933
|
-
}
|
|
4096
|
+
};
|
|
4097
|
+
process.stdin.prependListener("keypress", _replKeypressHandler);
|
|
3934
4098
|
}
|
|
3935
4099
|
|
|
3936
4100
|
logger.log(chalk.bold("\nChainlessChain Agent"));
|
|
@@ -4007,7 +4171,9 @@ async function startAgentReplInWorkspace(options = {}, startupAdmission) {
|
|
|
4007
4171
|
});
|
|
4008
4172
|
// Custom command wins; otherwise the built-in context-usage render.
|
|
4009
4173
|
if (_slCfg) {
|
|
4010
|
-
return _sl.renderStatusLine(_slCfg, context, {
|
|
4174
|
+
return _sl.renderStatusLine(_slCfg, context, {
|
|
4175
|
+
cwd: process.cwd(),
|
|
4176
|
+
});
|
|
4011
4177
|
}
|
|
4012
4178
|
const line = _sl.renderDefaultStatusLine(context);
|
|
4013
4179
|
return line && line.trim() ? line : null;
|
|
@@ -4206,6 +4372,19 @@ async function startAgentReplInWorkspace(options = {}, startupAdmission) {
|
|
|
4206
4372
|
return;
|
|
4207
4373
|
}
|
|
4208
4374
|
|
|
4375
|
+
// The interaction commands are registered through the shared registry,
|
|
4376
|
+
// while the mature REPL handlers below remain on their existing direct
|
|
4377
|
+
// paths. --disable-slash-commands replaces `trimmed` with a sentinel, so
|
|
4378
|
+
// it cannot accidentally reach this dispatcher.
|
|
4379
|
+
if (trimmed.startsWith("/")) {
|
|
4380
|
+
const commandName = trimmed.split(/\s+/, 1)[0];
|
|
4381
|
+
if (_promptInteractionSurface.commandNames.has(commandName)) {
|
|
4382
|
+
await _promptInteractionSurface.dispatchSlash(trimmed);
|
|
4383
|
+
prompt();
|
|
4384
|
+
return;
|
|
4385
|
+
}
|
|
4386
|
+
}
|
|
4387
|
+
|
|
4209
4388
|
if (trimmed === "/help") {
|
|
4210
4389
|
logger.log(chalk.bold("\nCommands:"));
|
|
4211
4390
|
logger.log(
|
|
@@ -4264,6 +4443,21 @@ async function startAgentReplInWorkspace(options = {}, startupAdmission) {
|
|
|
4264
4443
|
logger.log(
|
|
4265
4444
|
` ${chalk.cyan("/output-style")} Response persona (/output-style <name|list>; explanatory/learning built-in)`,
|
|
4266
4445
|
);
|
|
4446
|
+
logger.log(
|
|
4447
|
+
` ${chalk.cyan("/editor")} Edit a draft prompt in an external editor`,
|
|
4448
|
+
);
|
|
4449
|
+
logger.log(
|
|
4450
|
+
` ${chalk.cyan("/stash")} Stash/list/pop/clear draft prompts`,
|
|
4451
|
+
);
|
|
4452
|
+
logger.log(
|
|
4453
|
+
` ${chalk.cyan("/recap")} Show a lightweight session recap`,
|
|
4454
|
+
);
|
|
4455
|
+
logger.log(
|
|
4456
|
+
` ${chalk.cyan("/suggestions")} Prompt suggestions on|off|status|refresh`,
|
|
4457
|
+
);
|
|
4458
|
+
logger.log(
|
|
4459
|
+
` ${chalk.cyan("/paste-image")} Attach a clipboard image from a supporting host`,
|
|
4460
|
+
);
|
|
4267
4461
|
logger.log(
|
|
4268
4462
|
` ${chalk.cyan("/config")} Show config; ${chalk.cyan("/config <key>")} read, ${chalk.cyan("/config <key>=<val>")} set, ${chalk.cyan("/config --help")} keys`,
|
|
4269
4463
|
);
|
|
@@ -4313,7 +4507,7 @@ async function startAgentReplInWorkspace(options = {}, startupAdmission) {
|
|
|
4313
4507
|
` ${chalk.cyan("/add-dir")} Add an extra working root (/add-dir <dir>; no arg lists roots)`,
|
|
4314
4508
|
);
|
|
4315
4509
|
logger.log(
|
|
4316
|
-
` ${chalk.cyan("/reload-skills")}
|
|
4510
|
+
` ${chalk.cyan("/reload-skills")} Revoke process Skill grants and re-scan layers`,
|
|
4317
4511
|
);
|
|
4318
4512
|
logger.log(
|
|
4319
4513
|
` ${chalk.cyan("/reload-plugins")} Re-scan installed plugins after add/trust/upgrade`,
|
|
@@ -5173,7 +5367,11 @@ async function startAgentReplInWorkspace(options = {}, startupAdmission) {
|
|
|
5173
5367
|
dispatchAsyncHooks(
|
|
5174
5368
|
_settingsHooks,
|
|
5175
5369
|
"CwdChanged",
|
|
5176
|
-
{
|
|
5370
|
+
{
|
|
5371
|
+
old_cwd: oldCwd,
|
|
5372
|
+
cwd: newCwd,
|
|
5373
|
+
session_id: sessionId || null,
|
|
5374
|
+
},
|
|
5177
5375
|
{ cwd: newCwd, supervisor: _asyncHookSupervisor },
|
|
5178
5376
|
);
|
|
5179
5377
|
} catch {
|
|
@@ -5737,6 +5935,11 @@ async function startAgentReplInWorkspace(options = {}, startupAdmission) {
|
|
|
5737
5935
|
// layers (incl. .claude/skills) without restarting the session.
|
|
5738
5936
|
if (trimmed === "/reload-skills") {
|
|
5739
5937
|
try {
|
|
5938
|
+
_replSkillLoader.revokeExecutionAuthorizations?.({
|
|
5939
|
+
message:
|
|
5940
|
+
"Skill execution authorization was revoked by /reload-skills",
|
|
5941
|
+
reasonCode: "reload-skills",
|
|
5942
|
+
});
|
|
5740
5943
|
_replSkillLoader.clearCache();
|
|
5741
5944
|
const n = _replSkillLoader.loadAll().length;
|
|
5742
5945
|
_replBaseSystem = _buildReplBaseSystem();
|
|
@@ -5746,7 +5949,7 @@ async function startAgentReplInWorkspace(options = {}, startupAdmission) {
|
|
|
5746
5949
|
_persistReplContextSources();
|
|
5747
5950
|
logger.log(
|
|
5748
5951
|
chalk.green(
|
|
5749
|
-
`✔ 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)`,
|
|
5750
5953
|
),
|
|
5751
5954
|
);
|
|
5752
5955
|
} catch (err) {
|
|
@@ -5959,7 +6162,9 @@ async function startAgentReplInWorkspace(options = {}, startupAdmission) {
|
|
|
5959
6162
|
logger.log(chalk.gray(l));
|
|
5960
6163
|
} else {
|
|
5961
6164
|
// set — createReplGoal throws on a malformed spec (caught below).
|
|
5962
|
-
_sessionGoal = rg.createReplGoal(cmd.spec, {
|
|
6165
|
+
_sessionGoal = rg.createReplGoal(cmd.spec, {
|
|
6166
|
+
now: () => Date.now(),
|
|
6167
|
+
});
|
|
5963
6168
|
for (const l of rg.renderGoalStart(_sessionGoal))
|
|
5964
6169
|
logger.log(chalk.cyan(l));
|
|
5965
6170
|
}
|
|
@@ -7364,6 +7569,35 @@ async function startAgentReplInWorkspace(options = {}, startupAdmission) {
|
|
|
7364
7569
|
return;
|
|
7365
7570
|
}
|
|
7366
7571
|
|
|
7572
|
+
// Explicit clipboard attachment chips are consumed only when a real model
|
|
7573
|
+
// turn is about to start. They compose with auto-detected path images and
|
|
7574
|
+
// enter the same provider-neutral multimodal message/vision override.
|
|
7575
|
+
const clipboardImageChips = _promptInteractions.takeClipboardImageChips();
|
|
7576
|
+
const clipboardMerge = mergeClipboardImageChips(
|
|
7577
|
+
_userMessageContent,
|
|
7578
|
+
clipboardImageChips,
|
|
7579
|
+
);
|
|
7580
|
+
if (clipboardMerge.attached > 0) {
|
|
7581
|
+
_userMessageContent = clipboardMerge.content;
|
|
7582
|
+
if (!_visionLlm) {
|
|
7583
|
+
_visionLlm = resolveVisionLlm({
|
|
7584
|
+
hasImage: true,
|
|
7585
|
+
flags: {},
|
|
7586
|
+
llm: {
|
|
7587
|
+
provider,
|
|
7588
|
+
baseUrl,
|
|
7589
|
+
apiKey,
|
|
7590
|
+
visionModel: _visionModel,
|
|
7591
|
+
},
|
|
7592
|
+
});
|
|
7593
|
+
}
|
|
7594
|
+
logger.info(
|
|
7595
|
+
chalk.gray(
|
|
7596
|
+
`[image] ${clipboardMerge.attached} clipboard image(s) attached -> vision model ${_visionLlm.model}`,
|
|
7597
|
+
),
|
|
7598
|
+
);
|
|
7599
|
+
}
|
|
7600
|
+
|
|
7367
7601
|
// Add user message (keep the object ref so /note-next can be injected for
|
|
7368
7602
|
// this turn's model call and then stripped before persistence).
|
|
7369
7603
|
const _userMsg = { role: "user", content: _userMessageContent };
|
|
@@ -7426,7 +7660,17 @@ async function startAgentReplInWorkspace(options = {}, startupAdmission) {
|
|
|
7426
7660
|
.map(([k, v]) => `${k}: ${v}`);
|
|
7427
7661
|
// Append context to the last user message
|
|
7428
7662
|
const lastMsg = messages[messages.length - 1];
|
|
7429
|
-
|
|
7663
|
+
const slotContext = `[Context - user provided: ${parts.join(", ")}]`;
|
|
7664
|
+
if (Array.isArray(lastMsg.content)) {
|
|
7665
|
+
const textPart = lastMsg.content.find(
|
|
7666
|
+
(part) => part?.type === "text",
|
|
7667
|
+
);
|
|
7668
|
+
if (textPart)
|
|
7669
|
+
textPart.text = `${textPart.text}\n\n${slotContext}`;
|
|
7670
|
+
else lastMsg.content.unshift({ type: "text", text: slotContext });
|
|
7671
|
+
} else {
|
|
7672
|
+
lastMsg.content = `${lastMsg.content}\n\n${slotContext}`;
|
|
7673
|
+
}
|
|
7430
7674
|
logger.info(chalk.gray(`[slot-fill] ${parts.join(", ")}`));
|
|
7431
7675
|
}
|
|
7432
7676
|
}
|
|
@@ -7530,22 +7774,29 @@ async function startAgentReplInWorkspace(options = {}, startupAdmission) {
|
|
|
7530
7774
|
// reasoning dimmed before it. Skipped (left undefined) in replay mode.
|
|
7531
7775
|
let _liveStreamed = false;
|
|
7532
7776
|
let _liveThinkStarted = false;
|
|
7777
|
+
const _settleLiveWrite = (blocked) =>
|
|
7778
|
+
blocked ? _replOutputFlow.wait() : undefined;
|
|
7533
7779
|
const liveOpts = _streamLive
|
|
7534
7780
|
? {
|
|
7535
7781
|
onToken: (t) => {
|
|
7536
7782
|
// Separate the answer from the dimmed reasoning above it (once).
|
|
7537
|
-
|
|
7538
|
-
|
|
7783
|
+
let blocked = false;
|
|
7784
|
+
if (!_liveStreamed && _liveThinkStarted) {
|
|
7785
|
+
blocked = process.stdout.write("\n") === false;
|
|
7786
|
+
}
|
|
7539
7787
|
_liveStreamed = true;
|
|
7540
|
-
process.stdout.write(t);
|
|
7788
|
+
blocked = process.stdout.write(t) === false || blocked;
|
|
7789
|
+
return _settleLiveWrite(blocked);
|
|
7541
7790
|
},
|
|
7542
7791
|
onThinking: (t) => {
|
|
7543
7792
|
if (process.env.CC_REPL_THINKING === "0") return;
|
|
7793
|
+
let blocked = false;
|
|
7544
7794
|
if (!_liveThinkStarted) {
|
|
7545
7795
|
process.stdout.write(chalk.dim("💭 "));
|
|
7546
7796
|
_liveThinkStarted = true;
|
|
7547
7797
|
}
|
|
7548
|
-
process.stdout.write(chalk.dim(t));
|
|
7798
|
+
blocked = process.stdout.write(chalk.dim(t)) === false || blocked;
|
|
7799
|
+
return _settleLiveWrite(blocked);
|
|
7549
7800
|
},
|
|
7550
7801
|
}
|
|
7551
7802
|
: {};
|
|
@@ -7577,7 +7828,9 @@ async function startAgentReplInWorkspace(options = {}, startupAdmission) {
|
|
|
7577
7828
|
if (_settingsHooks && !_asyncHookSupervisor) {
|
|
7578
7829
|
const { AsyncHookSupervisor } =
|
|
7579
7830
|
await import("../lib/async-hook-supervisor.js");
|
|
7580
|
-
_asyncHookSupervisor = new AsyncHookSupervisor({
|
|
7831
|
+
_asyncHookSupervisor = new AsyncHookSupervisor({
|
|
7832
|
+
persistStats: true,
|
|
7833
|
+
});
|
|
7581
7834
|
}
|
|
7582
7835
|
const activeMcpRuntime = _activateMcpHostRuntime();
|
|
7583
7836
|
const activeRawMcpClient = activeMcpRuntime.rawClient || undefined;
|
|
@@ -7587,6 +7840,7 @@ async function startAgentReplInWorkspace(options = {}, startupAdmission) {
|
|
|
7587
7840
|
thinking: reasoning,
|
|
7588
7841
|
} = await agentLoop(messages, {
|
|
7589
7842
|
...liveOpts,
|
|
7843
|
+
waitForOutput: _replOutputFlow.wait,
|
|
7590
7844
|
mergeRoles: _mergeRolesThisTurn,
|
|
7591
7845
|
// Visible auto-retry feedback (Claude-Code 2.1.181): when the model's
|
|
7592
7846
|
// streaming call hits a transient connection drop and retries, tell the
|
|
@@ -7868,6 +8122,15 @@ async function startAgentReplInWorkspace(options = {}, startupAdmission) {
|
|
|
7868
8122
|
);
|
|
7869
8123
|
}
|
|
7870
8124
|
|
|
8125
|
+
// Generate follow-up suggestions from the settled assistant turn. The
|
|
8126
|
+
// controller snapshots the actual conversation context and debounces in
|
|
8127
|
+
// the background, so readline and persistence remain non-blocking.
|
|
8128
|
+
const suggestionRun = _promptInteractions.scheduleSuggestions({
|
|
8129
|
+
messages: messages.slice(),
|
|
8130
|
+
lastAssistantText: effectiveResponse || "",
|
|
8131
|
+
});
|
|
8132
|
+
void suggestionRun.promise;
|
|
8133
|
+
|
|
7871
8134
|
// Auto-save session
|
|
7872
8135
|
if (sessionId) {
|
|
7873
8136
|
try {
|
|
@@ -8012,6 +8275,7 @@ async function startAgentReplInWorkspace(options = {}, startupAdmission) {
|
|
|
8012
8275
|
};
|
|
8013
8276
|
|
|
8014
8277
|
rl.on("line", async (input) => {
|
|
8278
|
+
if (!(await _waitForReplOutput())) return;
|
|
8015
8279
|
if (_processingLine) {
|
|
8016
8280
|
const concurrentBtw = _slashCommandsDisabled
|
|
8017
8281
|
? null
|
|
@@ -8035,10 +8299,12 @@ async function startAgentReplInWorkspace(options = {}, startupAdmission) {
|
|
|
8035
8299
|
_processingLine = true;
|
|
8036
8300
|
try {
|
|
8037
8301
|
await handleLine(input);
|
|
8302
|
+
if (!(await _waitForReplOutput())) return;
|
|
8038
8303
|
while (_pendingLines.length) {
|
|
8039
8304
|
const next = _pendingLines.shift();
|
|
8040
8305
|
logger.log(chalk.cyan(`▶ running queued input: ${next}`));
|
|
8041
8306
|
await handleLine(next);
|
|
8307
|
+
if (!(await _waitForReplOutput())) return;
|
|
8042
8308
|
}
|
|
8043
8309
|
} finally {
|
|
8044
8310
|
_processingLine = false;
|
|
@@ -8046,6 +8312,18 @@ async function startAgentReplInWorkspace(options = {}, startupAdmission) {
|
|
|
8046
8312
|
});
|
|
8047
8313
|
|
|
8048
8314
|
rl.on("close", async () => {
|
|
8315
|
+
if (_replCleanupStarted) return;
|
|
8316
|
+
_replCleanupStarted = true;
|
|
8317
|
+
_replClosing = true;
|
|
8318
|
+
_promptInteractions.dispose();
|
|
8319
|
+
if (process.stdin.isTTY) {
|
|
8320
|
+
if (_replKeypressHandler) {
|
|
8321
|
+
process.stdin.removeListener("keypress", _replKeypressHandler);
|
|
8322
|
+
}
|
|
8323
|
+
for (const listener of _rlKeypressListeners) {
|
|
8324
|
+
process.stdin.removeListener("keypress", listener);
|
|
8325
|
+
}
|
|
8326
|
+
}
|
|
8049
8327
|
// Leave the alternate screen buffer first so the terminal is restored to
|
|
8050
8328
|
// the user's scrollback no matter how the REPL exits.
|
|
8051
8329
|
if (_tuiMode === "fullscreen" && process.stdout.isTTY) {
|
|
@@ -8224,6 +8502,31 @@ async function startAgentReplInWorkspace(options = {}, startupAdmission) {
|
|
|
8224
8502
|
} catch (_e) {
|
|
8225
8503
|
// Non-critical
|
|
8226
8504
|
}
|
|
8227
|
-
|
|
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);
|
|
8228
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
|
+
}
|
|
8229
8532
|
}
|