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/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
|
}
|
package/src/repl/chat-repl.js
CHANGED
|
@@ -52,6 +52,8 @@ import {
|
|
|
52
52
|
appendTokenUsage,
|
|
53
53
|
appendEvent,
|
|
54
54
|
} from "../harness/jsonl-session-store.js";
|
|
55
|
+
import { installPipeSafety } from "../runtime/pipe-safety.js";
|
|
56
|
+
import { installOutputBackpressure } from "../runtime/output-backpressure.js";
|
|
55
57
|
|
|
56
58
|
const SLASH_COMMANDS = {
|
|
57
59
|
"/exit": "Exit the chat",
|
|
@@ -96,6 +98,8 @@ export async function startChatRepl(options = {}) {
|
|
|
96
98
|
const apiKey = options.apiKey || null;
|
|
97
99
|
|
|
98
100
|
const messages = [];
|
|
101
|
+
const stdout = options.stdout || process.stdout;
|
|
102
|
+
const stderr = options.stderr || process.stderr;
|
|
99
103
|
|
|
100
104
|
// Phase J — attach chat REPL to a JSONL session so token_usage is recorded
|
|
101
105
|
// and `cc session usage` / `usage.*` WS routes show real numbers.
|
|
@@ -112,12 +116,42 @@ export async function startChatRepl(options = {}) {
|
|
|
112
116
|
}
|
|
113
117
|
}
|
|
114
118
|
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
119
|
+
let rl = null;
|
|
120
|
+
let closing = false;
|
|
121
|
+
let cleanupStarted = false;
|
|
122
|
+
let outputFailure = null;
|
|
123
|
+
const requestOutputClose = (error) => {
|
|
124
|
+
if (error && !outputFailure) outputFailure = error;
|
|
125
|
+
if (error) process.exitCode = error.code === "EPIPE" ? 0 : 1;
|
|
126
|
+
if (closing) return;
|
|
127
|
+
closing = true;
|
|
128
|
+
rl?.close?.();
|
|
129
|
+
};
|
|
130
|
+
const outputFlow = installOutputBackpressure({
|
|
131
|
+
stdout,
|
|
132
|
+
stderr,
|
|
133
|
+
maxQueuedBytes: options.outputBackpressureMaxBytes,
|
|
134
|
+
onFailure: requestOutputClose,
|
|
120
135
|
});
|
|
136
|
+
const disposePipeSafety = installPipeSafety([stdout, stderr], () =>
|
|
137
|
+
requestOutputClose(
|
|
138
|
+
Object.assign(new Error("chat REPL output pipe closed"), {
|
|
139
|
+
code: "EPIPE",
|
|
140
|
+
}),
|
|
141
|
+
),
|
|
142
|
+
);
|
|
143
|
+
try {
|
|
144
|
+
rl = readline.createInterface({
|
|
145
|
+
input: options.input || process.stdin,
|
|
146
|
+
output: stdout,
|
|
147
|
+
prompt: chalk.green("you> "),
|
|
148
|
+
terminal: true,
|
|
149
|
+
});
|
|
150
|
+
} catch (error) {
|
|
151
|
+
disposePipeSafety();
|
|
152
|
+
outputFlow.restore();
|
|
153
|
+
throw error;
|
|
154
|
+
}
|
|
121
155
|
|
|
122
156
|
logger.log(chalk.bold("\nChainlessChain AI Chat"));
|
|
123
157
|
logger.log(chalk.gray(`Model: ${model} Provider: ${provider}`));
|
|
@@ -127,6 +161,11 @@ export async function startChatRepl(options = {}) {
|
|
|
127
161
|
rl.prompt();
|
|
128
162
|
|
|
129
163
|
rl.on("line", async (input) => {
|
|
164
|
+
try {
|
|
165
|
+
await outputFlow.wait();
|
|
166
|
+
} catch {
|
|
167
|
+
return;
|
|
168
|
+
}
|
|
130
169
|
const trimmed = input.trim();
|
|
131
170
|
|
|
132
171
|
if (!trimmed) {
|
|
@@ -225,11 +264,14 @@ export async function startChatRepl(options = {}) {
|
|
|
225
264
|
messages.push({ role: "user", content: userContent });
|
|
226
265
|
|
|
227
266
|
// Stream the response
|
|
228
|
-
|
|
267
|
+
stdout.write(chalk.blue("ai> "));
|
|
229
268
|
|
|
230
269
|
try {
|
|
231
270
|
let response;
|
|
232
|
-
const onToken = (token) =>
|
|
271
|
+
const onToken = (token) => {
|
|
272
|
+
const accepted = stdout.write(token);
|
|
273
|
+
return accepted === false ? outputFlow.wait() : undefined;
|
|
274
|
+
};
|
|
233
275
|
let capturedUsage = null;
|
|
234
276
|
const onUsage = (u) => {
|
|
235
277
|
capturedUsage = u;
|
|
@@ -246,7 +288,7 @@ export async function startChatRepl(options = {}) {
|
|
|
246
288
|
const timeoutIn =
|
|
247
289
|
timeoutMs > ms ? Math.round((timeoutMs - ms) / 1000) : 0;
|
|
248
290
|
const suffix = timeoutIn > 0 ? ` · will time out in ${timeoutIn}s` : "";
|
|
249
|
-
|
|
291
|
+
stderr.write(
|
|
250
292
|
`\x1b[2m ⏳ waiting for API response (silent ${silent}s)${suffix}…\x1b[0m\n`,
|
|
251
293
|
);
|
|
252
294
|
};
|
|
@@ -310,7 +352,7 @@ export async function startChatRepl(options = {}) {
|
|
|
310
352
|
);
|
|
311
353
|
}
|
|
312
354
|
|
|
313
|
-
|
|
355
|
+
stdout.write("\n\n");
|
|
314
356
|
messages.push({ role: "assistant", content: response });
|
|
315
357
|
|
|
316
358
|
if (sessionId) {
|
|
@@ -331,14 +373,37 @@ export async function startChatRepl(options = {}) {
|
|
|
331
373
|
}
|
|
332
374
|
}
|
|
333
375
|
} catch (err) {
|
|
334
|
-
|
|
376
|
+
if (err?.isOutputBackpressureFailure === true) return;
|
|
377
|
+
stdout.write("\n");
|
|
335
378
|
logger.error(`Error: ${err.message}`);
|
|
336
379
|
}
|
|
337
380
|
|
|
381
|
+
try {
|
|
382
|
+
await outputFlow.wait();
|
|
383
|
+
} catch {
|
|
384
|
+
return;
|
|
385
|
+
}
|
|
338
386
|
rl.prompt();
|
|
339
387
|
});
|
|
340
388
|
|
|
341
|
-
rl.on("close", () => {
|
|
342
|
-
|
|
389
|
+
rl.on("close", async () => {
|
|
390
|
+
if (cleanupStarted) return;
|
|
391
|
+
cleanupStarted = true;
|
|
392
|
+
closing = true;
|
|
393
|
+
if (!outputFailure) {
|
|
394
|
+
try {
|
|
395
|
+
await outputFlow.wait();
|
|
396
|
+
} catch (error) {
|
|
397
|
+
outputFailure ||= error;
|
|
398
|
+
}
|
|
399
|
+
}
|
|
400
|
+
const exitCode = outputFailure
|
|
401
|
+
? outputFailure.code === "EPIPE"
|
|
402
|
+
? 0
|
|
403
|
+
: 1
|
|
404
|
+
: 0;
|
|
405
|
+
disposePipeSafety();
|
|
406
|
+
outputFlow.restore();
|
|
407
|
+
process.exit(exitCode);
|
|
343
408
|
});
|
|
344
409
|
}
|
|
@@ -21,6 +21,7 @@ import {
|
|
|
21
21
|
readClipboardImageChip,
|
|
22
22
|
} from "./clipboard-image.js";
|
|
23
23
|
import { layoutTerminalText } from "./terminal-layout.js";
|
|
24
|
+
import { SlashCommandRegistry } from "./slash-command-registry.js";
|
|
24
25
|
|
|
25
26
|
function commandArgs(line, command) {
|
|
26
27
|
const text = String(line || "");
|
|
@@ -80,6 +81,29 @@ export function registerPromptInteractionCommands(registry, controller) {
|
|
|
80
81
|
return PROMPT_INTERACTION_SLASH_COMMANDS.map((command) => command.name);
|
|
81
82
|
}
|
|
82
83
|
|
|
84
|
+
/** Build one session-local controller + registry dispatcher for production. */
|
|
85
|
+
export function createPromptInteractionSurface(options = {}) {
|
|
86
|
+
const controller =
|
|
87
|
+
options.controller || new PromptInteractionController(options);
|
|
88
|
+
const registry = options.registry || new SlashCommandRegistry();
|
|
89
|
+
const commandNames = new Set(
|
|
90
|
+
registerPromptInteractionCommands(registry, controller),
|
|
91
|
+
);
|
|
92
|
+
return {
|
|
93
|
+
controller,
|
|
94
|
+
registry,
|
|
95
|
+
commandNames,
|
|
96
|
+
async dispatchSlash(line) {
|
|
97
|
+
const text = String(line || "").trim();
|
|
98
|
+
const commandName = text.split(/\s+/, 1)[0];
|
|
99
|
+
if (!commandNames.has(commandName)) return { handled: false };
|
|
100
|
+
const command = registry.getCommand(commandName);
|
|
101
|
+
const args = text.slice(commandName.length).trim();
|
|
102
|
+
return command.handler(args);
|
|
103
|
+
},
|
|
104
|
+
};
|
|
105
|
+
}
|
|
106
|
+
|
|
83
107
|
export class PromptInteractionController {
|
|
84
108
|
constructor(options = {}) {
|
|
85
109
|
this.readline = options.readline || null;
|
|
@@ -96,6 +120,10 @@ export class PromptInteractionController {
|
|
|
96
120
|
typeof options.getSessionId === "function"
|
|
97
121
|
? options.getSessionId
|
|
98
122
|
: () => options.sessionId || null;
|
|
123
|
+
this.getSuggestionContext =
|
|
124
|
+
typeof options.getSuggestionContext === "function"
|
|
125
|
+
? options.getSuggestionContext
|
|
126
|
+
: () => ({});
|
|
99
127
|
this.screenReader = options.screenReader === true;
|
|
100
128
|
this.getColumns =
|
|
101
129
|
typeof options.getColumns === "function"
|
|
@@ -160,6 +188,22 @@ export class PromptInteractionController {
|
|
|
160
188
|
this._print(`\n${renderPromptSuggestions(suggestions)}`, { refresh: true });
|
|
161
189
|
}
|
|
162
190
|
|
|
191
|
+
_suggestionContext(overrides = {}) {
|
|
192
|
+
let live = {};
|
|
193
|
+
try {
|
|
194
|
+
const value = this.getSuggestionContext();
|
|
195
|
+
if (value && typeof value === "object") live = value;
|
|
196
|
+
} catch {
|
|
197
|
+
// Suggestion context is optional polish and cannot break prompt input.
|
|
198
|
+
}
|
|
199
|
+
return {
|
|
200
|
+
...live,
|
|
201
|
+
...overrides,
|
|
202
|
+
sessionId:
|
|
203
|
+
overrides.sessionId || live.sessionId || this.getSessionId() || null,
|
|
204
|
+
};
|
|
205
|
+
}
|
|
206
|
+
|
|
163
207
|
diagnostics() {
|
|
164
208
|
return {
|
|
165
209
|
keybindingErrors: this.keybindingDiagnostics.slice(),
|
|
@@ -219,7 +263,7 @@ export class PromptInteractionController {
|
|
|
219
263
|
const result = runPromptSuggestionsCommand(suggestionArgs, {
|
|
220
264
|
controller: this.suggestions,
|
|
221
265
|
persistEnabled: this.persistSuggestionEnabled,
|
|
222
|
-
context:
|
|
266
|
+
context: this._suggestionContext(),
|
|
223
267
|
});
|
|
224
268
|
this._print(result.message, { error: !result.ok });
|
|
225
269
|
return { handled: true, ...result };
|
|
@@ -321,10 +365,7 @@ export class PromptInteractionController {
|
|
|
321
365
|
}
|
|
322
366
|
|
|
323
367
|
scheduleSuggestions(context = {}) {
|
|
324
|
-
return this.suggestions.schedule(
|
|
325
|
-
...context,
|
|
326
|
-
sessionId: context.sessionId || this.getSessionId(),
|
|
327
|
-
});
|
|
368
|
+
return this.suggestions.schedule(this._suggestionContext(context));
|
|
328
369
|
}
|
|
329
370
|
|
|
330
371
|
takeClipboardImageChips() {
|
|
@@ -339,6 +380,25 @@ export class PromptInteractionController {
|
|
|
339
380
|
}
|
|
340
381
|
}
|
|
341
382
|
|
|
342
|
-
|
|
343
|
-
|
|
383
|
+
/**
|
|
384
|
+
* Merge host-provided clipboard image chips into a prepared user message.
|
|
385
|
+
* Only bounded data-image blocks produced by clipboard-image.js are accepted;
|
|
386
|
+
* a host cannot smuggle a remote URL into the model through this seam.
|
|
387
|
+
*/
|
|
388
|
+
export function mergeClipboardImageChips(content, chips = []) {
|
|
389
|
+
const accepted = (Array.isArray(chips) ? chips : []).filter(
|
|
390
|
+
(chip) =>
|
|
391
|
+
chip?.type === "image_url" &&
|
|
392
|
+
/^data:image\/(?:png|jpeg|gif|webp);base64,[A-Za-z0-9+/]+={0,2}$/u.test(
|
|
393
|
+
String(chip.image_url?.url || ""),
|
|
394
|
+
),
|
|
395
|
+
);
|
|
396
|
+
if (!accepted.length) return { content, attached: 0 };
|
|
397
|
+
const parts = Array.isArray(content)
|
|
398
|
+
? content.slice()
|
|
399
|
+
: String(content || "")
|
|
400
|
+
? [{ type: "text", text: String(content) }]
|
|
401
|
+
: [];
|
|
402
|
+
parts.push(...accepted);
|
|
403
|
+
return { content: parts, attached: accepted.length };
|
|
344
404
|
}
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
6
|
import { formatBackgroundTasks } from "./tasks-status.js";
|
|
7
|
-
import {
|
|
7
|
+
import { buildDoctorChecks, renderDoctor } from "./doctor-status.js";
|
|
8
8
|
|
|
9
9
|
export class SlashCommandRegistry {
|
|
10
10
|
static _instance = null;
|
|
@@ -124,9 +124,8 @@ export class SlashCommandRegistry {
|
|
|
124
124
|
[
|
|
125
125
|
"/doctor",
|
|
126
126
|
"Check system health",
|
|
127
|
-
|
|
128
|
-
write(
|
|
129
|
-
await runDoctorChecks(write);
|
|
127
|
+
(args, { write, doctorInput = {} }) => {
|
|
128
|
+
write(renderDoctor(buildDoctorChecks(doctorInput)));
|
|
130
129
|
},
|
|
131
130
|
],
|
|
132
131
|
[
|