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.
Files changed (215) hide show
  1. package/README.md +29 -6
  2. package/completions/_cc +2 -2
  3. package/completions/cc.bash +2 -2
  4. package/completions/cc.fish +2 -2
  5. package/completions/cc.ps1 +2 -2
  6. package/package.json +1 -1
  7. package/src/assets/web-panel/assets/{AIOps-DBLkaTje.js → AIOps-CSN-D2xb.js} +1 -1
  8. package/src/assets/web-panel/assets/{ActionButton-BAy-Qebo.js → ActionButton-CBSMlGzv.js} +1 -1
  9. package/src/assets/web-panel/assets/{Analytics-Dfb1Mlau.js → Analytics-3yr3Nt2t.js} +3 -3
  10. package/src/assets/web-panel/assets/{AppLayout-BbFkN8m0.js → AppLayout-1b-o3lMH.js} +4 -4
  11. package/src/assets/web-panel/assets/{Artifacts-kM4-cEEH.js → Artifacts-Dr8rJi17.js} +1 -1
  12. package/src/assets/web-panel/assets/{Audit-BSQfYWf3.js → Audit-2V689HC2.js} +1 -1
  13. package/src/assets/web-panel/assets/{BackgroundAgents-CWDbI_fG.js → BackgroundAgents-DIxkNrD_.js} +1 -1
  14. package/src/assets/web-panel/assets/{Backup-BxLPbq_Q.js → Backup-Pg6BcwrE.js} +1 -1
  15. package/src/assets/web-panel/assets/{BaseInput-DuIbdrp6.js → BaseInput-CLUpqyfH.js} +1 -1
  16. package/src/assets/web-panel/assets/{Chat-NEmjGahU.js → Chat-qVS8rD4G.js} +5 -5
  17. package/src/assets/web-panel/assets/{ChatBubbleRenderer-DfJEo-5_.js → ChatBubbleRenderer-ti49X12Q.js} +1 -1
  18. package/src/assets/web-panel/assets/{Checkbox-CaoeG3Ns.js → Checkbox-Bs9AaEay.js} +1 -1
  19. package/src/assets/web-panel/assets/{Codegen-BFf_g3xY.js → Codegen-Dvc1mq8q.js} +1 -1
  20. package/src/assets/web-panel/assets/{Col-CNOd8mdQ.js → Col-C4_biO7A.js} +1 -1
  21. package/src/assets/web-panel/assets/{Community-BhpDqz-i.js → Community-al25XQUV.js} +1 -1
  22. package/src/assets/web-panel/assets/{Compact-CQ42T7Z6.js → Compact-D-ZDB8M1.js} +1 -1
  23. package/src/assets/web-panel/assets/{Compliance-DI5PcLMN.js → Compliance-6lIfDz1W.js} +1 -1
  24. package/src/assets/web-panel/assets/{Cowork-wYYZe0wR.js → Cowork-DdI48XMJ.js} +3 -3
  25. package/src/assets/web-panel/assets/{Cron-Dlq03DD_.js → Cron-DF5HZd2g.js} +2 -2
  26. package/src/assets/web-panel/assets/{Crosschain-BzqS8PP3.js → Crosschain-DuxeSvt3.js} +1 -1
  27. package/src/assets/web-panel/assets/{DID-It55Yem7.js → DID-B9yHkUTd.js} +2 -2
  28. package/src/assets/web-panel/assets/{Dashboard-9HkH6oxn.js → Dashboard-Z5EuI3IM.js} +2 -2
  29. package/src/assets/web-panel/assets/{Dropdown-00e2YWRB.js → Dropdown-vuRfO3dY.js} +1 -1
  30. package/src/assets/web-panel/assets/{EmailListRenderer-By-ugRDL.js → EmailListRenderer-QFLCc1uL.js} +1 -1
  31. package/src/assets/web-panel/assets/{FamilyGuardDashboard-C396yu5m.js → FamilyGuardDashboard-BZ7EXkFJ.js} +1 -1
  32. package/src/assets/web-panel/assets/{Federation-BME5sbZY.js → Federation-DycIEg2s.js} +1 -1
  33. package/src/assets/web-panel/assets/{FormItemContext-B4TEZCr-.js → FormItemContext-Ba0H_7yG.js} +1 -1
  34. package/src/assets/web-panel/assets/{GenericCardRenderer-_9CX9ieY.js → GenericCardRenderer--JgSTK-C.js} +1 -1
  35. package/src/assets/web-panel/assets/{Git-BrS6WVSr.js → Git-DEvTbldE.js} +2 -2
  36. package/src/assets/web-panel/assets/{Governance-BMdCWnKF.js → Governance-BJIljy18.js} +1 -1
  37. package/src/assets/web-panel/assets/{Inference-Bcp3vzcA.js → Inference-CVyW3G3q.js} +1 -1
  38. package/src/assets/web-panel/assets/{KnowledgeGraph-DlLuqGY5.js → KnowledgeGraph-v8-borge.js} +1 -1
  39. package/src/assets/web-panel/assets/{Logs-D-W_6x09.js → Logs-BCdS7gGo.js} +2 -2
  40. package/src/assets/web-panel/assets/{MarkdownRenderer-DElGPxCZ.js → MarkdownRenderer-CU-JB2Ns.js} +1 -1
  41. package/src/assets/web-panel/assets/{Marketplace-Dzn2guvI.js → Marketplace-AN6tGA2B.js} +1 -1
  42. package/src/assets/web-panel/assets/{McpTools-uPict-KL.js → McpTools-DGViXASN.js} +6 -6
  43. package/src/assets/web-panel/assets/{Memory-BCw43kOb.js → Memory-Bpvmx9X4.js} +2 -2
  44. package/src/assets/web-panel/assets/{MobileBridge-DmV8Wv7F.js → MobileBridge-CTP-DOTC.js} +1 -1
  45. package/src/assets/web-panel/assets/{MobileProjects-C-HaCmSM.js → MobileProjects-C8qfktxn.js} +1 -1
  46. package/src/assets/web-panel/assets/{Mtc-C6jxJ34Y.js → Mtc-ndYr0M1s.js} +5 -5
  47. package/src/assets/web-panel/assets/{MtcAudit-Za8ExwhQ.js → MtcAudit-Ci0-YK7W.js} +5 -5
  48. package/src/assets/web-panel/assets/{Multisig-KKDbuEgb.js → Multisig-BehSTFW7.js} +3 -3
  49. package/src/assets/web-panel/assets/{NLProgramming-Dj97zbCk.js → NLProgramming-B91_YBLv.js} +1 -1
  50. package/src/assets/web-panel/assets/{Notes-DBXvMraU.js → Notes-BQlJ0-gq.js} +4 -4
  51. package/src/assets/web-panel/assets/{NotificationSettings-DOxITLYl.js → NotificationSettings-CDgYMjYK.js} +1 -1
  52. package/src/assets/web-panel/assets/{OrderTableRenderer-7DVjZwMM.js → OrderTableRenderer-BJkSlKeH.js} +1 -1
  53. package/src/assets/web-panel/assets/{Organization-D1i73mjb.js → Organization-B-b3cWCp.js} +4 -4
  54. package/src/assets/web-panel/assets/{Overflow-kW6Q3rvg.js → Overflow-CILUL4nL.js} +1 -1
  55. package/src/assets/web-panel/assets/{P2P-C-v_8p2v.js → P2P-BTCFBkF0.js} +2 -2
  56. package/src/assets/web-panel/assets/{PdhVaultBrowser-tUbbY64W.js → PdhVaultBrowser-BAR6AUx5.js} +5 -5
  57. package/src/assets/web-panel/assets/{Permissions-DkHxEZ58.js → Permissions-RRf6HbWJ.js} +4 -4
  58. package/src/assets/web-panel/assets/{PersonalDataHub-DWuMPGrK.js → PersonalDataHub-BGTMiICa.js} +4 -4
  59. package/src/assets/web-panel/assets/{Pipeline-30IYXWWm.js → Pipeline-945qY9YF.js} +1 -1
  60. package/src/assets/web-panel/assets/{Privacy-DZNQ4RrH.js → Privacy-DPZwI6lJ.js} +1 -1
  61. package/src/assets/web-panel/assets/{ProjectInit-De2MCElf.js → ProjectInit-BPH2NRKc.js} +2 -2
  62. package/src/assets/web-panel/assets/{ProjectSettings-5uHk9wsC.js → ProjectSettings-BZCRNCvb.js} +2 -2
  63. package/src/assets/web-panel/assets/{Projects-XjDfrFLb.js → Projects-BTI__tBt.js} +1 -1
  64. package/src/assets/web-panel/assets/{Providers-Bgwj6gcE.js → Providers-DDCf_nIw.js} +1 -1
  65. package/src/assets/web-panel/assets/{QrScannerModal-gKql9umv.js → QrScannerModal-N6rbqt1r.js} +1 -1
  66. package/src/assets/web-panel/assets/{QuickAsk-BWvmwkh7.js → QuickAsk-DrwjPS7M.js} +1 -1
  67. package/src/assets/web-panel/assets/{Recommend-Uok4hZPd.js → Recommend-Cog9Wd99.js} +1 -1
  68. package/src/assets/web-panel/assets/{RemoteSession-DLJzTk9a.js → RemoteSession-DC9pynjM.js} +3 -3
  69. package/src/assets/web-panel/assets/{Reputation-ZVwIPMSO.js → Reputation-D4th4gKS.js} +1 -1
  70. package/src/assets/web-panel/assets/{Row-CN04HxWR.js → Row-ogiWR6v9.js} +1 -1
  71. package/src/assets/web-panel/assets/{RssFeed-BdSrEG4g.js → RssFeed-uYlk-2Oj.js} +3 -3
  72. package/src/assets/web-panel/assets/{Search-5PwVd0IR.js → Search-DYzzwvtM.js} +1 -1
  73. package/src/assets/web-panel/assets/{Security-DdBo4zjN.js → Security-Da7YOyX6.js} +4 -4
  74. package/src/assets/web-panel/assets/{Services-D3letU7M.js → Services-CLI1VM1X.js} +2 -2
  75. package/src/assets/web-panel/assets/{Skeleton--Jb83f1v.js → Skeleton-DK77dzDC.js} +1 -1
  76. package/src/assets/web-panel/assets/{Skills-Bg-CulHX.js → Skills-C_z7Lmhl.js} +1 -1
  77. package/src/assets/web-panel/assets/{Sla-7QOa7S-X.js → Sla-D6413Q8L.js} +1 -1
  78. package/src/assets/web-panel/assets/{SpeechSettings-DQwppjRw.js → SpeechSettings-C07ydbj2.js} +1 -1
  79. package/src/assets/web-panel/assets/{SyncSettings-dNPQGL-s.js → SyncSettings-DGOm2W22.js} +2 -2
  80. package/src/assets/web-panel/assets/{Tasks-Ggs-Z48q.js → Tasks-Cr_PVyLM.js} +1 -1
  81. package/src/assets/web-panel/assets/{Templates-78TYHgRO.js → Templates-BhL2mZ0t.js} +1 -1
  82. package/src/assets/web-panel/assets/{Tenant-xHwwv9vO.js → Tenant-CPk5oyUr.js} +1 -1
  83. package/src/assets/web-panel/assets/{Terminal-BJXSJfm4.js → Terminal-ClhqUsqV.js} +2 -2
  84. package/src/assets/web-panel/assets/{TimelineRenderer-fLbjRhJw.js → TimelineRenderer-BCC7MV5a.js} +1 -1
  85. package/src/assets/web-panel/assets/{Tokens-oMSBSBmB.js → Tokens-DD1Jt4oO.js} +1 -1
  86. package/src/assets/web-panel/assets/{Trigger-CfnY_wX_.js → Trigger-BCydFvN2.js} +1 -1
  87. package/src/assets/web-panel/assets/{Trust-AdyTgYKO.js → Trust-DDHT1noa.js} +1 -1
  88. package/src/assets/web-panel/assets/{UkeySign-C5K_81dS.js → UkeySign-CWPZC7oI.js} +1 -1
  89. package/src/assets/web-panel/assets/{VideoEditing-vcGVDqki.js → VideoEditing-9Ea0nJR0.js} +1 -1
  90. package/src/assets/web-panel/assets/{Wallet-hhOc3hlY.js → Wallet-BjxhLxTf.js} +4 -4
  91. package/src/assets/web-panel/assets/{WebAuthn-CVloC11s.js → WebAuthn-C6uCye67.js} +4 -4
  92. package/src/assets/web-panel/assets/{WorkflowEditor-CYeE32R5.js → WorkflowEditor-d2g4reYo.js} +1 -1
  93. package/src/assets/web-panel/assets/{chat-CNROR5bN.js → chat-frTc6ScX.js} +1 -1
  94. package/src/assets/web-panel/assets/{colors-Cjx-cnwU.js → colors-B5K6lG3v.js} +1 -1
  95. package/src/assets/web-panel/assets/{compact-item-DOIfelvD.js → compact-item-Cdn46r8D.js} +1 -1
  96. package/src/assets/web-panel/assets/{createContext-DfA7UZ8u.js → createContext-DU1jqVQi.js} +1 -1
  97. package/src/assets/web-panel/assets/devWarning-CP-jEUyw.js +1 -0
  98. package/src/assets/web-panel/assets/{hasIn-BMSCzcfM.js → hasIn-DdNH-aW_.js} +1 -1
  99. package/src/assets/web-panel/assets/{index-CAnvhwg6.js → index-32ajXmTb.js} +1 -1
  100. package/src/assets/web-panel/assets/{index-BEMwBBHk.js → index-7ehytj1K.js} +1 -1
  101. package/src/assets/web-panel/assets/{index-CVL27kXC.js → index-BC4eGkar.js} +1 -1
  102. package/src/assets/web-panel/assets/{index-FBc_CJBd.js → index-BVLL-mq_.js} +1 -1
  103. package/src/assets/web-panel/assets/{index-dCd8cSUq.js → index-BXKhhx9V.js} +1 -1
  104. package/src/assets/web-panel/assets/{index-BemJ2u_E.js → index-BcGwGIaR.js} +1 -1
  105. package/src/assets/web-panel/assets/{index-CVm1_i3t.js → index-Be4OtL-N.js} +1 -1
  106. package/src/assets/web-panel/assets/{index-Cs6l7uRu.js → index-BoFak7le.js} +1 -1
  107. package/src/assets/web-panel/assets/{index-DPAWBrMf.js → index-BwlIxAmX.js} +1 -1
  108. package/src/assets/web-panel/assets/{index-DLmn0BoK.js → index-CGiwfwB9.js} +1 -1
  109. package/src/assets/web-panel/assets/{index-B7gf77CJ.js → index-CHmkkdfT.js} +1 -1
  110. package/src/assets/web-panel/assets/{index-m2vG9gG_.js → index-CQorP1Ng.js} +1 -1
  111. package/src/assets/web-panel/assets/{index-ClNMrqEY.js → index-CUoNAmE8.js} +1 -1
  112. package/src/assets/web-panel/assets/{index-DHtpHZAQ.js → index-Cj6P9Nst.js} +1 -1
  113. package/src/assets/web-panel/assets/{index-C2K9SNVX.js → index-CtRPi4YC.js} +1 -1
  114. package/src/assets/web-panel/assets/index-CzrCHrFy.js +1 -0
  115. package/src/assets/web-panel/assets/{index-BolaOQxx.js → index-D1qIC4GR.js} +1 -1
  116. package/src/assets/web-panel/assets/{index-BM1y_9hM.js → index-D23xGYjM.js} +1 -1
  117. package/src/assets/web-panel/assets/{index-Cyweh0Hy.js → index-D4l82Me1.js} +1 -1
  118. package/src/assets/web-panel/assets/{index-By61ojUx.js → index-D4miNkWv.js} +1 -1
  119. package/src/assets/web-panel/assets/{index-BbQ2eQ8H.js → index-DIVyTmZ3.js} +1 -1
  120. package/src/assets/web-panel/assets/{index-Icdoqmzt.js → index-DM5FmPZU.js} +1 -1
  121. package/src/assets/web-panel/assets/{index-DjEsxO5h.js → index-DRzBBdQ0.js} +1 -1
  122. package/src/assets/web-panel/assets/{index-B4iYzpkL.js → index-Dca_i7H8.js} +1 -1
  123. package/src/assets/web-panel/assets/{index-C3L5gdiD.js → index-DftZuyA7.js} +1 -1
  124. package/src/assets/web-panel/assets/{index-CdYBye8B.js → index-DtBCCkC4.js} +1 -1
  125. package/src/assets/web-panel/assets/{index-AWg-0M1S.js → index-HjCvXHf5.js} +1 -1
  126. package/src/assets/web-panel/assets/{index-CEKMcOyU.js → index-LaivuXyF.js} +1 -1
  127. package/src/assets/web-panel/assets/{index-BxOk9vkG.js → index-MM4Zz2OE.js} +1 -1
  128. package/src/assets/web-panel/assets/{index-CNC5onm4.js → index-NmEXVjzt.js} +1 -1
  129. package/src/assets/web-panel/assets/index-OJsGskZw.js +1 -0
  130. package/src/assets/web-panel/assets/{index-BUonmKSE.js → index-PjAS0sIZ.js} +1 -1
  131. package/src/assets/web-panel/assets/{index-B5Udeb_6.js → index-VDM2KKo9.js} +1 -1
  132. package/src/assets/web-panel/assets/{index-5-HXgOX5.js → index-cYaWV4Mq.js} +1 -1
  133. package/src/assets/web-panel/assets/{index-DBpWXFed.js → index-gg3QPC3i.js} +3 -3
  134. package/src/assets/web-panel/assets/{index-C7xn0Nub.js → index-kVO_MDm-.js} +1 -1
  135. package/src/assets/web-panel/assets/{index-ti_lnTWl.js → index-mgU3Q-lN.js} +1 -1
  136. package/src/assets/web-panel/assets/{index-CshQbilz.js → index-t_OBzIP4.js} +1 -1
  137. package/src/assets/web-panel/assets/{index-mTpHoEzr.js → index-yZRuZpwK.js} +1 -1
  138. package/src/assets/web-panel/assets/{initDefaultProps-Bu8k7H1Y.js → initDefaultProps-DwLTRNdo.js} +1 -1
  139. package/src/assets/web-panel/assets/{motion-s3iVK41k.js → motion-BchZDXnh.js} +1 -1
  140. package/src/assets/web-panel/assets/{move-B6B-E5Jz.js → move-e8M-zZFl.js} +1 -1
  141. package/src/assets/web-panel/assets/{mtc-parser-BiYgTidA.js → mtc-parser-Ci40fQb3.js} +1 -1
  142. package/src/assets/web-panel/assets/{omit-pU1RdUDA.js → omit-C7110_f2.js} +1 -1
  143. package/src/assets/web-panel/assets/{pickAttrs-Cdoq_SeM.js → pickAttrs-skteMgnM.js} +1 -1
  144. package/src/assets/web-panel/assets/{placementArrow-DIuvAn2K.js → placementArrow-MGZ-lh2B.js} +1 -1
  145. package/src/assets/web-panel/assets/{responsiveObserve-8w16YR2D.js → responsiveObserve-N4Sd7uy-.js} +1 -1
  146. package/src/assets/web-panel/assets/{slide-VZ01cZe3.js → slide-C7HDGRaa.js} +1 -1
  147. package/src/assets/web-panel/assets/{statusUtils-lh8YMN8Z.js → statusUtils-BhRexlgy.js} +1 -1
  148. package/src/assets/web-panel/assets/{styleChecker-CmDyreec.js → styleChecker-ChEoQkN2.js} +1 -1
  149. package/src/assets/web-panel/assets/{useFlexGapSupport-hiJWMU5y.js → useFlexGapSupport-CDbG1s2Y.js} +1 -1
  150. package/src/assets/web-panel/assets/{useFs-Thnu-Iaj.js → useFs-CUIx0tcJ.js} +1 -1
  151. package/src/assets/web-panel/assets/{usePersonalDataHub-LBdOxjwY.js → usePersonalDataHub-bkO7L7ec.js} +1 -1
  152. package/src/assets/web-panel/assets/{vnode-Cg6lFY_X.js → vnode-B_ZvABIG.js} +1 -1
  153. package/src/assets/web-panel/assets/{zoom-DVfsR1YO.js → zoom-D5shjpS8.js} +1 -1
  154. package/src/assets/web-panel/index.html +1 -1
  155. package/src/command-help-index.json +4 -4
  156. package/src/command-manifest.json +214 -50
  157. package/src/command-surface-policy.js +115 -0
  158. package/src/commands/agent.js +3 -3
  159. package/src/commands/background-session.js +145 -0
  160. package/src/commands/mcp.js +125 -7
  161. package/src/commands/serve.js +1 -1
  162. package/src/commands/session-show.js +131 -0
  163. package/src/commands/session.js +50 -106
  164. package/src/commands/skill.js +87 -50
  165. package/src/data/changelog.json +18 -5
  166. package/src/gateways/ws/ws-agent-handler.js +8 -1
  167. package/src/harness/jsonl-session-store.js +428 -25
  168. package/src/harness/mcp-client.js +2652 -404
  169. package/src/harness/session-list-index.js +65 -7
  170. package/src/lazy-dispatch.js +76 -8
  171. package/src/lib/abort-utils.js +68 -0
  172. package/src/lib/agent-core.js +1 -59
  173. package/src/lib/chat-core.js +24 -6
  174. package/src/lib/cowork-mcp-tools.js +47 -2
  175. package/src/lib/cowork-task-runner.js +11 -3
  176. package/src/lib/doctor-checkup.js +9 -7
  177. package/src/lib/learning/skill-improver.js +21 -2
  178. package/src/lib/mcp-call-ledger-store.js +116 -3
  179. package/src/lib/mcp-call-ledger.js +59 -14
  180. package/src/lib/mcp-client.js +1 -0
  181. package/src/lib/mcp-stdio-executable-identity.js +598 -0
  182. package/src/lib/mcp-stdio-execution-authority.js +363 -0
  183. package/src/lib/mcp-tool-metadata.js +436 -0
  184. package/src/lib/mcp-tool-result.js +367 -0
  185. package/src/lib/observability/index.js +71 -0
  186. package/src/lib/pr-link-ledger.js +8 -27
  187. package/src/lib/pr-link-store.js +26 -0
  188. package/src/lib/process-execution-broker/index.js +14 -0
  189. package/src/lib/process-tree-termination.js +253 -0
  190. package/src/lib/public-ide-capability-manifest.js +6 -0
  191. package/src/lib/session-host-snapshot.js +9 -5
  192. package/src/lib/session-projection.js +5 -1
  193. package/src/lib/session-tail.js +31 -1
  194. package/src/lib/skill-budget.js +222 -0
  195. package/src/lib/skill-execution-authority.js +381 -0
  196. package/src/lib/skill-execution-identity.js +39 -2
  197. package/src/lib/skill-loader.js +322 -86
  198. package/src/lib/skill-mcp.js +43 -1
  199. package/src/repl/agent-repl.js +349 -46
  200. package/src/repl/bg-dashboard.js +81 -28
  201. package/src/repl/chat-repl.js +77 -12
  202. package/src/repl/prompt-interactions.js +67 -7
  203. package/src/repl/slash-command-registry.js +3 -4
  204. package/src/runtime/agent-core.js +340 -108
  205. package/src/runtime/agent-runtime.js +26 -3
  206. package/src/runtime/headless-runner.js +150 -23
  207. package/src/runtime/headless-stream.js +309 -59
  208. package/src/runtime/mcp-config.js +157 -10
  209. package/src/runtime/mcp-tool-search.js +0 -0
  210. package/src/runtime/output-backpressure.js +366 -0
  211. package/src/runtime/pipe-safety.js +56 -21
  212. package/src/assets/web-panel/assets/devWarning-ChcCBGs7.js +0 -1
  213. package/src/assets/web-panel/assets/index-Q2IajL54.js +0 -1
  214. package/src/assets/web-panel/assets/index-bRkHgOMc.js +0 -1
  215. package/src/lib/skill-process-broker.js +0 -85
@@ -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(process.stdout.isTTY && process.stdin.isTTY),
177
- write: (s) => process.stdout.write(s),
178
- enterAlt: () => process.stdout.write(ALT_ENTER),
179
- exitAlt: () => process.stdout.write(ALT_EXIT),
180
- clear: () => process.stdout.write(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 (process.stdin.isTTY && process.stdin.setRawMode) {
183
- process.stdin.setRawMode(on);
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(process.stdin);
189
- process.stdin.resume();
211
+ readline.emitKeypressEvents(input);
212
+ input.resume();
190
213
  const handler = (str, key) => onKey(str, key || {});
191
- process.stdin.on("keypress", handler);
192
- return () => process.stdin.off("keypress", handler);
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: process.stdin,
198
- output: process.stdout,
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
- model = buildDashboardModel(listAgents(), {
234
- filter: FILTERS[filterIndex],
235
- now: now(),
236
- });
237
- if (selected >= model.flat.length)
238
- selected = Math.max(0, model.flat.length - 1);
239
- if (peek && !model.flat.some((s) => s.id === peek.id)) peek = null;
240
- io.clear();
241
- io.write(
242
- renderDashboard(model, { selectedIndex: selected, peek, message }) + "\n",
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
  }
@@ -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
- const rl = readline.createInterface({
116
- input: process.stdin,
117
- output: process.stdout,
118
- prompt: chalk.green("you> "),
119
- terminal: true,
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
- process.stdout.write(chalk.blue("ai> "));
267
+ stdout.write(chalk.blue("ai> "));
229
268
 
230
269
  try {
231
270
  let response;
232
- const onToken = (token) => process.stdout.write(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
- process.stderr.write(
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
- process.stdout.write("\n\n");
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
- process.stdout.write("\n");
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
- process.exit(0);
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: optionsContext(this.getSessionId()),
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
- function optionsContext(sessionId) {
343
- return { sessionId: sessionId || null };
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 { runDoctorChecks } from "./doctor-status.js";
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
- async (args, { write }) => {
128
- write("Running system diagnostics...");
129
- await runDoctorChecks(write);
127
+ (args, { write, doctorInput = {} }) => {
128
+ write(renderDoctor(buildDoctorChecks(doctorInput)));
130
129
  },
131
130
  ],
132
131
  [