chainlesschain 0.162.153 → 0.162.155

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 (204) hide show
  1. package/README.md +1 -1
  2. package/package.json +1 -1
  3. package/src/assets/web-panel/.build-hash +1 -1
  4. package/src/assets/web-panel/assets/{AIOps-CBilhs1R.js → AIOps-Cth_zZeA.js} +1 -1
  5. package/src/assets/web-panel/assets/{ActionButton-Cf-RnC5Y.js → ActionButton-zfwA3Oxn.js} +1 -1
  6. package/src/assets/web-panel/assets/{Analytics-S8tkGhw0.js → Analytics-FeKc4aU2.js} +3 -3
  7. package/src/assets/web-panel/assets/AppLayout-C6WIrDZk.js +9 -0
  8. package/src/assets/web-panel/assets/{Audit-DF2zVFZ0.js → Audit-C2gRq5Qi.js} +1 -1
  9. package/src/assets/web-panel/assets/BackgroundAgents-Dq2340Kl.css +1 -0
  10. package/src/assets/web-panel/assets/BackgroundAgents-TeTIFiW-.js +1 -0
  11. package/src/assets/web-panel/assets/{Backup-CMEiwQZL.js → Backup-CLJk3k6i.js} +1 -1
  12. package/src/assets/web-panel/assets/{BaseInput-rwLw4K70.js → BaseInput-5ms537NY.js} +1 -1
  13. package/src/assets/web-panel/assets/{Chat-C478H8u9.js → Chat-4Nr5U9PJ.js} +5 -5
  14. package/src/assets/web-panel/assets/ChatBubbleRenderer-BFEnA8LN.js +1 -0
  15. package/src/assets/web-panel/assets/{Checkbox-D-47X635.js → Checkbox-B2Bca5ev.js} +1 -1
  16. package/src/assets/web-panel/assets/{Codegen-Cb9Ihed5.js → Codegen-D6Uysdbv.js} +1 -1
  17. package/src/assets/web-panel/assets/{Col-CQsdzczt.js → Col-B3XQ8xc7.js} +1 -1
  18. package/src/assets/web-panel/assets/{Community-DiqzeAy-.js → Community-CqwKtoPz.js} +1 -1
  19. package/src/assets/web-panel/assets/{Compact-Bq-APyyF.js → Compact-BZZINTus.js} +1 -1
  20. package/src/assets/web-panel/assets/{Compliance-Dnb1YxPr.js → Compliance-Dr3BXkWA.js} +1 -1
  21. package/src/assets/web-panel/assets/{Cowork-CZljerdQ.js → Cowork-rx_x9Wqc.js} +3 -3
  22. package/src/assets/web-panel/assets/{Cron-gAdbZOq7.js → Cron-BpZr7rHc.js} +2 -2
  23. package/src/assets/web-panel/assets/{Crosschain-DMPGXttD.js → Crosschain-CeV5EYVg.js} +1 -1
  24. package/src/assets/web-panel/assets/{DID-C7dBDAv8.js → DID-fe2Qaowz.js} +2 -2
  25. package/src/assets/web-panel/assets/{Dashboard-ByR4VcF8.js → Dashboard-Fh_EdWVa.js} +2 -2
  26. package/src/assets/web-panel/assets/{Dropdown-Bc4tbiQN.js → Dropdown-N1PIwWYf.js} +1 -1
  27. package/src/assets/web-panel/assets/{EmailListRenderer-BhUN2pDw.js → EmailListRenderer-B6vK93Ed.js} +1 -1
  28. package/src/assets/web-panel/assets/{FamilyGuardDashboard-DpJuANCD.js → FamilyGuardDashboard-e3iUZhAg.js} +1 -1
  29. package/src/assets/web-panel/assets/{Federation-BEm4TvcE.js → Federation-uInc9YcN.js} +1 -1
  30. package/src/assets/web-panel/assets/{FormItemContext-JnkPkhiC.js → FormItemContext-DmQZSX0z.js} +1 -1
  31. package/src/assets/web-panel/assets/{GenericCardRenderer-BfCzFUIC.js → GenericCardRenderer-CV_Z8OAo.js} +1 -1
  32. package/src/assets/web-panel/assets/{Git-1c0MWpmD.js → Git-jXAhNN8d.js} +2 -2
  33. package/src/assets/web-panel/assets/{Governance-C_j3EBpv.js → Governance-Blw1esBB.js} +1 -1
  34. package/src/assets/web-panel/assets/{Inference-Cb7XRgtY.js → Inference-CoK8Ia0Z.js} +1 -1
  35. package/src/assets/web-panel/assets/{KnowledgeGraph-DoHPvHyK.js → KnowledgeGraph-5xrglMTK.js} +1 -1
  36. package/src/assets/web-panel/assets/{Logs-CYriKpy-.js → Logs-BGIG9798.js} +2 -2
  37. package/src/assets/web-panel/assets/{Marketplace-BT56-GD0.js → Marketplace-DFqhJMuY.js} +1 -1
  38. package/src/assets/web-panel/assets/{McpTools-B6XX_wnZ.js → McpTools-7U8tEhwK.js} +4 -4
  39. package/src/assets/web-panel/assets/{Memory-DRgNuaQc.js → Memory-BrvboPLE.js} +2 -2
  40. package/src/assets/web-panel/assets/{MobileBridge-B0qgvBN2.js → MobileBridge-nzcd7d1H.js} +1 -1
  41. package/src/assets/web-panel/assets/MobileProjects-DETVNKa7.js +1 -0
  42. package/src/assets/web-panel/assets/{Mtc-DnSFj9jw.js → Mtc-SZcCYec3.js} +4 -4
  43. package/src/assets/web-panel/assets/{MtcAudit-c0tuz1sm.js → MtcAudit-Cx_d3N17.js} +2 -2
  44. package/src/assets/web-panel/assets/{Multisig-CVLBGioX.js → Multisig-DtcmSUo-.js} +3 -3
  45. package/src/assets/web-panel/assets/{NLProgramming-CFaqmNbx.js → NLProgramming-Dgx2QHl-.js} +1 -1
  46. package/src/assets/web-panel/assets/{Notes-BM_mNwU_.js → Notes-oHTGEHoJ.js} +3 -3
  47. package/src/assets/web-panel/assets/{NotificationSettings-Sa1k3ReS.js → NotificationSettings-qKD-IlOl.js} +1 -1
  48. package/src/assets/web-panel/assets/OrderTableRenderer-CiU4Sy1J.js +1 -0
  49. package/src/assets/web-panel/assets/{Organization-C2blcq-P.js → Organization-BBNXnltP.js} +2 -2
  50. package/src/assets/web-panel/assets/{Overflow-B3PwOJnK.js → Overflow-Df2fDbvT.js} +1 -1
  51. package/src/assets/web-panel/assets/{OverrideContext-BlgL9440.js → OverrideContext-C0w0P4A3.js} +1 -1
  52. package/src/assets/web-panel/assets/{P2P-DaB9T6tb.js → P2P-DUHj6zjO.js} +2 -2
  53. package/src/assets/web-panel/assets/{PdhVaultBrowser-BpEBZ72n.js → PdhVaultBrowser-B2nWgdoV.js} +5 -5
  54. package/src/assets/web-panel/assets/{Permissions-BTkWInyH.js → Permissions-CiW0afny.js} +3 -3
  55. package/src/assets/web-panel/assets/{PersonalDataHub-LHEwNmyL.js → PersonalDataHub-cc1mHU_D.js} +2 -2
  56. package/src/assets/web-panel/assets/{Pipeline-DvWIxqH-.js → Pipeline-B93zTapU.js} +1 -1
  57. package/src/assets/web-panel/assets/{Privacy-BDbpBagU.js → Privacy-Db1jXWfd.js} +1 -1
  58. package/src/assets/web-panel/assets/{ProjectInit-DLeMDWWv.js → ProjectInit-DFoZ1h9U.js} +2 -2
  59. package/src/assets/web-panel/assets/{ProjectSettings-BK3NsxIz.js → ProjectSettings-BkkUnmYs.js} +2 -2
  60. package/src/assets/web-panel/assets/Projects-Xa779XLC.js +1 -0
  61. package/src/assets/web-panel/assets/{Providers-D2EvmPAd.js → Providers-DOF01IEN.js} +1 -1
  62. package/src/assets/web-panel/assets/{QrScannerModal-DAcu2fNg.js → QrScannerModal-TohRsKd0.js} +1 -1
  63. package/src/assets/web-panel/assets/{QuickAsk-B7U850Yt.js → QuickAsk-DjLcr1ZU.js} +1 -1
  64. package/src/assets/web-panel/assets/{Recommend-bxNXLzOR.js → Recommend--Ism6bMx.js} +1 -1
  65. package/src/assets/web-panel/assets/{RemoteSession-BsCjAfSJ.js → RemoteSession-B8CL3YHM.js} +2 -2
  66. package/src/assets/web-panel/assets/{Reputation-DZ9LPu-R.js → Reputation-CaRxB3Ep.js} +1 -1
  67. package/src/assets/web-panel/assets/{Row-cIYkCB3Y.js → Row-BioxZBBH.js} +1 -1
  68. package/src/assets/web-panel/assets/{RssFeed-BxTNTtTp.js → RssFeed-B2LipwH3.js} +2 -2
  69. package/src/assets/web-panel/assets/{Search-DSar6GhE.js → Search-BpjLBV8u.js} +1 -1
  70. package/src/assets/web-panel/assets/{Security-oCbIGysL.js → Security-TibHuJWP.js} +4 -4
  71. package/src/assets/web-panel/assets/{Services-D8yojrNv.js → Services-DtkuWseW.js} +2 -2
  72. package/src/assets/web-panel/assets/{Skeleton-vTA6P1eL.js → Skeleton-BIcPQae6.js} +1 -1
  73. package/src/assets/web-panel/assets/Skills-DoTWIQOX.js +1 -0
  74. package/src/assets/web-panel/assets/{Sla-Cpwxzz__.js → Sla-uUSU185m.js} +1 -1
  75. package/src/assets/web-panel/assets/{SpeechSettings-DcPN9kCb.js → SpeechSettings-B7Bgc9mk.js} +1 -1
  76. package/src/assets/web-panel/assets/{SyncSettings-DY0U5Vuj.js → SyncSettings-K-XrJ2s9.js} +2 -2
  77. package/src/assets/web-panel/assets/Tasks-B3hEXNcI.js +1 -0
  78. package/src/assets/web-panel/assets/{Templates-MD3nfWGm.js → Templates-CaFnyGDK.js} +1 -1
  79. package/src/assets/web-panel/assets/{Tenant-DKVnyGXM.js → Tenant-C2X0PtEC.js} +1 -1
  80. package/src/assets/web-panel/assets/{Terminal-DQMIZfWf.js → Terminal-CKU5yVsT.js} +2 -2
  81. package/src/assets/web-panel/assets/{TimelineRenderer-CugmNZTs.js → TimelineRenderer-8trZeYDC.js} +1 -1
  82. package/src/assets/web-panel/assets/{Tokens-Cjvlttu6.js → Tokens-BUEEe9US.js} +1 -1
  83. package/src/assets/web-panel/assets/{Trigger-CzNOEcoI.js → Trigger-COU97I7Y.js} +1 -1
  84. package/src/assets/web-panel/assets/{Trust-D5jg9iPA.js → Trust-DNWQChaP.js} +1 -1
  85. package/src/assets/web-panel/assets/{UkeySign-59bMlvxY.js → UkeySign-DWX_VtCp.js} +1 -1
  86. package/src/assets/web-panel/assets/{VideoEditing-DmCzSBqn.js → VideoEditing-D9eIDhsp.js} +1 -1
  87. package/src/assets/web-panel/assets/{Wallet-DoFe8V6W.js → Wallet-BOnCCgIL.js} +4 -4
  88. package/src/assets/web-panel/assets/{WebAuthn-9POLER-w.js → WebAuthn-DN4dD-W8.js} +5 -5
  89. package/src/assets/web-panel/assets/{WorkflowEditor-cFQH0Og6.js → WorkflowEditor-ClvDb_jX.js} +1 -1
  90. package/src/assets/web-panel/assets/{chat-D3fR2NkZ.js → chat-DIxQ_Eeb.js} +1 -1
  91. package/src/assets/web-panel/assets/{collapseMotion-DVfhbsdP.js → collapseMotion-CqM1QOwM.js} +1 -1
  92. package/src/assets/web-panel/assets/{colors-DiLEwE_P.js → colors-BOX3WKcP.js} +1 -1
  93. package/src/assets/web-panel/assets/{compact-item-CGfFDdic.js → compact-item-Bny82VDi.js} +1 -1
  94. package/src/assets/web-panel/assets/{createContext-BbIbOPL2.js → createContext-Cnoy3enS.js} +1 -1
  95. package/src/assets/web-panel/assets/devWarning-Dr9dzlQ5.js +1 -0
  96. package/src/assets/web-panel/assets/{echarts-CL0F2SnX.js → echarts-C61Vn3mP.js} +1 -1
  97. package/src/assets/web-panel/assets/{hasIn-DqyWp63_.js → hasIn-BJ7fkx3F.js} +1 -1
  98. package/src/assets/web-panel/assets/{icons-BsDmGpcT.js → icons-DEmcnxBf.js} +1 -1
  99. package/src/assets/web-panel/assets/{index-BtcjYHgn.js → index--CcGiwAI.js} +1 -1
  100. package/src/assets/web-panel/assets/{index-C2lZXIEA.js → index--Xb2lq6E.js} +1 -1
  101. package/src/assets/web-panel/assets/{index-DzuGkFvy.js → index-2N9Y1cLx.js} +1 -1
  102. package/src/assets/web-panel/assets/{index-DQH--Uw5.js → index-7GLXvG8p.js} +1 -1
  103. package/src/assets/web-panel/assets/{index-CweXQM71.js → index-B1-h2QrC.js} +1 -1
  104. package/src/assets/web-panel/assets/{index-kL8f4MRS.js → index-B6WoW8DJ.js} +1 -1
  105. package/src/assets/web-panel/assets/{index-BCoDp6o_.js → index-B7Fah_BX.js} +1 -1
  106. package/src/assets/web-panel/assets/index-B92M3t6B.js +1 -0
  107. package/src/assets/web-panel/assets/{index-D3vQv3Kq.js → index-BDkN6YOI.js} +1 -1
  108. package/src/assets/web-panel/assets/{index-tLaW5UzZ.js → index-BJ8-oLHI.js} +1 -1
  109. package/src/assets/web-panel/assets/{index-CToOzfR2.js → index-BM0mQhVv.js} +1 -1
  110. package/src/assets/web-panel/assets/{index-ns6bNnGo.js → index-BP7HFsAK.js} +1 -1
  111. package/src/assets/web-panel/assets/{index-CIleIMTT.js → index-BWPey0Uf.js} +1 -1
  112. package/src/assets/web-panel/assets/{index-BiLP6SNu.js → index-BbZynivw.js} +1 -1
  113. package/src/assets/web-panel/assets/{index-DdjXemK-.js → index-BcmXbIwv.js} +1 -1
  114. package/src/assets/web-panel/assets/{index-DPxmTldu.js → index-Bk5yST66.js} +1 -1
  115. package/src/assets/web-panel/assets/{index-Km36XQ3z.js → index-Bvheiep2.js} +1 -1
  116. package/src/assets/web-panel/assets/{index-DH6qaU1a.js → index-C--PPqwi.js} +1 -1
  117. package/src/assets/web-panel/assets/{index-qHvMk0U3.js → index-C1nHEOFE.js} +1 -1
  118. package/src/assets/web-panel/assets/{index-D16FSRMW.js → index-CEPTiBWy.js} +1 -1
  119. package/src/assets/web-panel/assets/{index-hVGbmc7y.js → index-CEq5Vh0i.js} +1 -1
  120. package/src/assets/web-panel/assets/{index-CZQshOyI.js → index-CN6emwbq.js} +1 -1
  121. package/src/assets/web-panel/assets/{index-Db9UJVjj.js → index-CQ-xLCVb.js} +1 -1
  122. package/src/assets/web-panel/assets/{index-ComvO4hy.js → index-CW6n-y1L.js} +1 -1
  123. package/src/assets/web-panel/assets/{index-DdLwxj-F.js → index-CZ6CHiCQ.js} +1 -1
  124. package/src/assets/web-panel/assets/{index-CySlHabI.js → index-CZCTan8Q.js} +1 -1
  125. package/src/assets/web-panel/assets/{index-BtwvOuvO.js → index-CnjtAKdw.js} +1 -1
  126. package/src/assets/web-panel/assets/{index-BKxvHCqz.js → index-D2qNdrNW.js} +1 -1
  127. package/src/assets/web-panel/assets/{index-CH8gSMSA.js → index-D9QEc7Tq.js} +1 -1
  128. package/src/assets/web-panel/assets/{index-FFlp8Jx9.js → index-DBhkJqVF.js} +1 -1
  129. package/src/assets/web-panel/assets/{index-BKeRBUtO.js → index-DSRIjAWO.js} +1 -1
  130. package/src/assets/web-panel/assets/{index-C1S30j-Z.js → index-DxX8hhmL.js} +1 -1
  131. package/src/assets/web-panel/assets/{index-fCfaVIr7.js → index-Kio1ekYg.js} +1 -1
  132. package/src/assets/web-panel/assets/{index-auI9fxYH.js → index-OaNFXV8N.js} +1 -1
  133. package/src/assets/web-panel/assets/{index-xm1KHUEQ.js → index-Ty4w18mR.js} +4 -4
  134. package/src/assets/web-panel/assets/{index-CHpI6g_S.js → index-ceH9DsAi.js} +1 -1
  135. package/src/assets/web-panel/assets/index-d2UcGpK-.js +1 -0
  136. package/src/assets/web-panel/assets/{index-rTrejDjk.js → index-qOBKrw-g.js} +1 -1
  137. package/src/assets/web-panel/assets/{index-ByE5tKjP.js → index-rn6xd12s.js} +1 -1
  138. package/src/assets/web-panel/assets/{initDefaultProps-pg_7pnd6.js → initDefaultProps-BmxMBuUZ.js} +1 -1
  139. package/src/assets/web-panel/assets/{motion-B5GoJSZL.js → motion-DnXNwo2k.js} +1 -1
  140. package/src/assets/web-panel/assets/{move-BaJoe2FW.js → move-WnIJwmZN.js} +1 -1
  141. package/src/assets/web-panel/assets/{mtc-parser-CiQGbqtr.js → mtc-parser-DAto-c2w.js} +1 -1
  142. package/src/assets/web-panel/assets/{omit-BFNEsKvU.js → omit-BtvW9W0H.js} +1 -1
  143. package/src/assets/web-panel/assets/{pickAttrs-YbSoANHm.js → pickAttrs-DFqI9-_C.js} +1 -1
  144. package/src/assets/web-panel/assets/{placementArrow-C40MJPIH.js → placementArrow-Dax6wOK3.js} +1 -1
  145. package/src/assets/web-panel/assets/{responsiveObserve-LaVz2zF6.js → responsiveObserve-BUFWWC83.js} +1 -1
  146. package/src/assets/web-panel/assets/{slide-BkSoAGxF.js → slide-DyARyuSz.js} +1 -1
  147. package/src/assets/web-panel/assets/{statusUtils-C4jWjohA.js → statusUtils-BZSDugpi.js} +1 -1
  148. package/src/assets/web-panel/assets/{styleChecker-BGqeIh-m.js → styleChecker-B7o_K-4R.js} +1 -1
  149. package/src/assets/web-panel/assets/useFlexGapSupport-Dg7Bg8VG.js +1 -0
  150. package/src/assets/web-panel/assets/{useFs-DOiP38RR.js → useFs-DQYXo5Rk.js} +1 -1
  151. package/src/assets/web-panel/assets/{useMergedState-Dc9sO2d7.js → useMergedState-DXbJa_zE.js} +1 -1
  152. package/src/assets/web-panel/assets/{usePersonalDataHub-C-nC0ymt.js → usePersonalDataHub-E8NBAnkq.js} +1 -1
  153. package/src/assets/web-panel/assets/{useRefs-CAeFRzPf.js → useRefs-BrCfMLMc.js} +1 -1
  154. package/src/assets/web-panel/assets/{useState-LwExUYDO.js → useState-CnxtbLoC.js} +1 -1
  155. package/src/assets/web-panel/assets/{vendor-BMxUs6UX.js → vendor-Bn1JzzAn.js} +1 -1
  156. package/src/assets/web-panel/assets/{vnode-BalilHOU.js → vnode-DD71_w1b.js} +1 -1
  157. package/src/assets/web-panel/assets/{zoom-DjUELqks.js → zoom-zc9EMd3q.js} +1 -1
  158. package/src/assets/web-panel/index.html +3 -3
  159. package/src/command-manifest.json +43 -1
  160. package/src/commands/agent.js +23 -2
  161. package/src/commands/agents.js +62 -1
  162. package/src/commands/ask.js +1 -1
  163. package/src/commands/auto-mode.js +87 -0
  164. package/src/commands/background-session.js +499 -0
  165. package/src/commands/command.js +4 -1
  166. package/src/commands/complete.js +147 -0
  167. package/src/commands/permissions.js +49 -0
  168. package/src/commands/remote-control.js +407 -0
  169. package/src/data/changelog.json +12 -2
  170. package/src/gateways/ws/background-agent-protocol.js +337 -0
  171. package/src/gateways/ws/message-dispatcher.js +10 -0
  172. package/src/gateways/ws/remote-session-protocol.js +102 -1
  173. package/src/gateways/ws/ws-server.js +61 -0
  174. package/src/harness/mcp-client.js +8 -0
  175. package/src/index.js +12 -0
  176. package/src/lib/auto-mode-config.js +396 -0
  177. package/src/lib/background-agent-supervisor.js +202 -17
  178. package/src/lib/background-session-transport.js +315 -0
  179. package/src/lib/permission-denial-store.js +159 -0
  180. package/src/lib/remote-approval-bridge.js +360 -0
  181. package/src/lib/remote-control.js +270 -0
  182. package/src/lib/repl-denials.js +26 -0
  183. package/src/lib/shell-approval.js +4 -0
  184. package/src/lib/ws-rpc-client.js +150 -0
  185. package/src/repl/agent-repl.js +208 -16
  186. package/src/repl/permission-tier.js +31 -0
  187. package/src/runtime/agent-core.js +57 -0
  188. package/src/runtime/headless-runner.js +126 -8
  189. package/src/runtime/headless-stream.js +87 -1
  190. package/src/runtime/mcp-config.js +11 -0
  191. package/src/runtime/mcp-tool-search.js +0 -0
  192. package/src/runtime/policies/agent-policy.js +17 -0
  193. package/src/workers/background-agent-worker.js +218 -37
  194. package/src/assets/web-panel/assets/AppLayout-CNXI3Bw5.js +0 -9
  195. package/src/assets/web-panel/assets/ChatBubbleRenderer-DNQsKN61.js +0 -1
  196. package/src/assets/web-panel/assets/MobileProjects-CRRfswf7.js +0 -1
  197. package/src/assets/web-panel/assets/OrderTableRenderer-lWzmtqBh.js +0 -1
  198. package/src/assets/web-panel/assets/Projects-BPWPg9zA.js +0 -1
  199. package/src/assets/web-panel/assets/Skills-BXLf_KgZ.js +0 -1
  200. package/src/assets/web-panel/assets/Tasks-DKu3-sZJ.js +0 -1
  201. package/src/assets/web-panel/assets/devWarning-B6FRgsmP.js +0 -1
  202. package/src/assets/web-panel/assets/index-Cq411qtc.js +0 -1
  203. package/src/assets/web-panel/assets/index-D1oX0DK3.js +0 -1
  204. package/src/assets/web-panel/assets/useFlexGapSupport-JsR5NMrM.js +0 -1
@@ -0,0 +1,499 @@
1
+ import { existsSync, readFileSync } from "node:fs";
2
+ import chalk from "chalk";
3
+ import { logger } from "../lib/logger.js";
4
+
5
+ function formatAge(ms) {
6
+ const n = Math.max(0, Math.round(Number(ms || 0) / 1000));
7
+ if (n < 60) return `${n}s`;
8
+ const m = Math.floor(n / 60);
9
+ const s = n % 60;
10
+ if (m < 60) return `${m}m${s ? ` ${s}s` : ""}`;
11
+ const h = Math.floor(m / 60);
12
+ const rest = m % 60;
13
+ return `${h}h${rest ? ` ${rest}m` : ""}`;
14
+ }
15
+
16
+ export function formatBackgroundAgentLine(session, now = Date.now()) {
17
+ const elapsed = session?.endedAt
18
+ ? session.endedAt - session.startedAt
19
+ : now - session.startedAt;
20
+ const title = session?.title ? ` ${session.title}` : "";
21
+ return `${session.id} ${String(session.status || "?").padEnd(9)} ${formatAge(elapsed).padStart(6)} ${session.cwd || ""}${title}`;
22
+ }
23
+
24
+ function printBackgroundAgents(sessions, { now = Date.now() } = {}) {
25
+ if (!sessions.length) {
26
+ logger.log(chalk.gray("No background agents."));
27
+ return;
28
+ }
29
+ for (const session of sessions) {
30
+ logger.log(formatBackgroundAgentLine(session, now));
31
+ if (session.pid || session.sessionId) {
32
+ logger.log(
33
+ chalk.gray(
34
+ ` pid ${session.pid || "?"}${session.sessionId ? ` session ${session.sessionId}` : ""}`,
35
+ ),
36
+ );
37
+ }
38
+ }
39
+ }
40
+
41
+ function formatTimestamp(value) {
42
+ const n = Number(value);
43
+ return Number.isFinite(n) && n > 0 ? new Date(n).toISOString() : "-";
44
+ }
45
+
46
+ export function formatBackgroundAgentDetails(
47
+ session,
48
+ logText = "",
49
+ options = {},
50
+ ) {
51
+ const now = typeof options.now === "number" ? options.now : Date.now();
52
+ const elapsed = session?.endedAt
53
+ ? session.endedAt - session.startedAt
54
+ : now - session.startedAt;
55
+ const lines = [
56
+ `Background agent ${session.id}`,
57
+ ` status: ${session.status || "?"}`,
58
+ ` title: ${session.title || "-"}`,
59
+ ` cwd: ${session.cwd || "-"}`,
60
+ ` pid: ${session.pid || "-"}`,
61
+ ` workerPid: ${session.workerPid || "-"}`,
62
+ ` agentPid: ${session.agentPid || "-"}`,
63
+ ` session: ${session.sessionId || "-"}`,
64
+ ` elapsed: ${formatAge(elapsed)}`,
65
+ ` startedAt: ${formatTimestamp(session.startedAt)}`,
66
+ ` endedAt: ${formatTimestamp(session.endedAt)}`,
67
+ ` log: ${session.logFile || "-"}`,
68
+ ];
69
+ if (session.phase) lines.push(` phase: ${session.phase}`);
70
+ if (Number.isFinite(Number(session.turnCount))) {
71
+ lines.push(` turns: ${session.turnCount}`);
72
+ }
73
+ if (session.transport?.pipe) {
74
+ lines.push(` transport: interactive attach available`);
75
+ }
76
+ if (session.lostReason) lines.push(` lostReason: ${session.lostReason}`);
77
+ if (Number.isFinite(Number(session.exitCode))) {
78
+ lines.push(` exitCode: ${session.exitCode}`);
79
+ }
80
+ lines.push("", "Recent output:");
81
+ lines.push(logText ? logText.trimEnd() : " (no log output)");
82
+ lines.push("", "Commands:");
83
+ lines.push(` cc attach ${session.id}`);
84
+ lines.push(` cc logs ${session.id} -n 100`);
85
+ if (session.status === "running") {
86
+ lines.push(` cc daemon stop ${session.id}`);
87
+ }
88
+ return lines.join("\n");
89
+ }
90
+
91
+ function parseLines(value, fallback) {
92
+ const n = Number(value);
93
+ return Number.isFinite(n) && n > 0 ? Math.floor(n) : fallback;
94
+ }
95
+
96
+ async function loadSupervisor() {
97
+ return import("../lib/background-agent-supervisor.js");
98
+ }
99
+
100
+ function readLogFromOffset(file, offset) {
101
+ if (!existsSync(file)) return { text: "", offset };
102
+ const text = readFileSync(file, "utf8");
103
+ if (offset > text.length) offset = 0;
104
+ return { text: text.slice(offset), offset: text.length };
105
+ }
106
+
107
+ async function followBackgroundAgent(id, options = {}) {
108
+ const {
109
+ readBackgroundAgentState,
110
+ effectiveBackgroundAgentState,
111
+ readBackgroundAgentLog,
112
+ logPath,
113
+ } = await loadSupervisor();
114
+ const lines = parseLines(options.lines, 100);
115
+ let state = effectiveBackgroundAgentState(readBackgroundAgentState(id));
116
+ if (!state) throw new Error(`Background agent not found: ${id}`);
117
+ const logFile = logPath(id);
118
+ const initial = readBackgroundAgentLog(id, { lines });
119
+ if (initial)
120
+ process.stdout.write(initial.endsWith("\n") ? initial : `${initial}\n`);
121
+ let offset = existsSync(logFile) ? readFileSync(logFile, "utf8").length : 0;
122
+
123
+ if (options.follow === false || state.status !== "running") {
124
+ return state;
125
+ }
126
+
127
+ logger.log(chalk.gray(`Attached to ${id}. Streaming logs; Ctrl-C detaches.`));
128
+ while (true) {
129
+ await new Promise((resolve) => setTimeout(resolve, 1000));
130
+ const next = readLogFromOffset(logFile, offset);
131
+ offset = next.offset;
132
+ if (next.text) process.stdout.write(next.text);
133
+ state = effectiveBackgroundAgentState(readBackgroundAgentState(id));
134
+ if (!state || state.status !== "running") {
135
+ if (state) {
136
+ logger.log(chalk.gray(`\n${id} is ${state.status}.`));
137
+ }
138
+ return state;
139
+ }
140
+ }
141
+ }
142
+
143
+ /**
144
+ * Interactive attach over the session transport: stream the log AND accept
145
+ * typed follow-up prompts (plus /stop · /status · /detach). Returns false
146
+ * when the transport is unavailable so the caller can fall back to the
147
+ * log-only follow.
148
+ */
149
+ async function interactiveAttach(id, state, options = {}) {
150
+ const { connectBackgroundSession } =
151
+ await import("../lib/background-session-transport.js");
152
+ const { readBackgroundAgentLog, logPath } = await loadSupervisor();
153
+
154
+ let closed = false;
155
+ let conn;
156
+ try {
157
+ conn = await connectBackgroundSession({
158
+ pipePath: state.transport.pipe,
159
+ token: state.transport.token,
160
+ onEvent: (message) => {
161
+ switch (message.type) {
162
+ case "turn-started":
163
+ logger.log(
164
+ chalk.gray(
165
+ `— turn ${message.turn} started${message.prompt ? `: ${message.prompt}` : ""}`,
166
+ ),
167
+ );
168
+ return;
169
+ case "turn-ended":
170
+ logger.log(
171
+ chalk.gray(
172
+ `— turn ${message.turn} ended (exit ${message.exitCode})`,
173
+ ),
174
+ );
175
+ return;
176
+ case "idle":
177
+ logger.log(
178
+ chalk.gray(
179
+ "— session idle; type a follow-up prompt, or Ctrl-C to detach (ends the session)",
180
+ ),
181
+ );
182
+ return;
183
+ case "accepted":
184
+ logger.log(chalk.gray(`— prompt queued (#${message.queued})`));
185
+ return;
186
+ case "status":
187
+ logger.log(
188
+ chalk.gray(
189
+ `— ${message.id} phase=${message.phase} turn=${message.turn}`,
190
+ ),
191
+ );
192
+ return;
193
+ case "stopping":
194
+ logger.log(chalk.gray("— stopping current turn"));
195
+ return;
196
+ case "closing":
197
+ return; // close handler prints the final state
198
+ case "error":
199
+ logger.log(chalk.yellow(`— ${message.message}`));
200
+ return;
201
+ default:
202
+ return;
203
+ }
204
+ },
205
+ onClose: () => {
206
+ closed = true;
207
+ },
208
+ });
209
+ } catch {
210
+ return false; // transport gone (worker finalizing/finalized) — fall back
211
+ }
212
+
213
+ const lines = parseLines(options.lines, 100);
214
+ const initial = readBackgroundAgentLog(id, { lines });
215
+ if (initial)
216
+ process.stdout.write(initial.endsWith("\n") ? initial : `${initial}\n`);
217
+ const logFile = logPath(id);
218
+ let offset = existsSync(logFile) ? readFileSync(logFile, "utf8").length : 0;
219
+ logger.log(
220
+ chalk.gray(
221
+ `Attached to ${id} (interactive). Type a prompt to continue the session; /stop · /status · Ctrl-C detaches.`,
222
+ ),
223
+ );
224
+
225
+ const readline = await import("node:readline");
226
+ const rl = readline.createInterface({
227
+ input: process.stdin,
228
+ output: process.stdout,
229
+ });
230
+ rl.on("line", (line) => {
231
+ const text = line.trim();
232
+ if (!text) return;
233
+ if (text === "/detach" || text === "/exit" || text === "/quit") {
234
+ rl.close();
235
+ return;
236
+ }
237
+ if (text === "/stop") {
238
+ conn.send({ type: "stop" });
239
+ return;
240
+ }
241
+ if (text === "/status") {
242
+ conn.send({ type: "status" });
243
+ return;
244
+ }
245
+ conn.send({ type: "prompt", text });
246
+ });
247
+ const detach = () => rl.close();
248
+ rl.on("SIGINT", detach);
249
+
250
+ await new Promise((resolve) => {
251
+ rl.on("close", resolve);
252
+ const poll = setInterval(() => {
253
+ const next = readLogFromOffset(logFile, offset);
254
+ offset = next.offset;
255
+ if (next.text) process.stdout.write(next.text);
256
+ if (closed) {
257
+ clearInterval(poll);
258
+ rl.close();
259
+ }
260
+ }, 500);
261
+ poll.unref?.();
262
+ rl.on("close", () => clearInterval(poll));
263
+ });
264
+
265
+ // Drain any output written between the last poll and detach.
266
+ const rest = readLogFromOffset(logFile, offset);
267
+ if (rest.text) process.stdout.write(rest.text);
268
+ try {
269
+ conn.close();
270
+ } catch {
271
+ /* already closed by the worker */
272
+ }
273
+ const { readBackgroundAgentState, effectiveBackgroundAgentState } =
274
+ await loadSupervisor();
275
+ const finalState = effectiveBackgroundAgentState(
276
+ readBackgroundAgentState(id),
277
+ );
278
+ if (finalState) logger.log(chalk.gray(`\n${id} is ${finalState.status}.`));
279
+ return true;
280
+ }
281
+
282
+ export function registerBackgroundSessionCommands(program) {
283
+ program
284
+ .command("logs <id>")
285
+ .description("Print recent output from a background agent")
286
+ .option("-n, --lines <n>", "Number of lines", "100")
287
+ .action(async (id, options) => {
288
+ try {
289
+ const { readBackgroundAgentState, readBackgroundAgentLog } =
290
+ await loadSupervisor();
291
+ if (!readBackgroundAgentState(id)) {
292
+ throw new Error(`Background agent not found: ${id}`);
293
+ }
294
+ process.stdout.write(
295
+ readBackgroundAgentLog(id, { lines: parseLines(options.lines, 100) }),
296
+ );
297
+ } catch (error) {
298
+ logger.error(chalk.red(error.message));
299
+ process.exitCode = 1;
300
+ }
301
+ });
302
+
303
+ program
304
+ .command("attach <id>")
305
+ .description(
306
+ "Attach to a background agent — interactive when its session transport is available, log stream otherwise",
307
+ )
308
+ .option("-n, --lines <n>", "Initial lines to print", "100")
309
+ .option("--no-follow", "Print the initial log tail and exit")
310
+ .option("--no-input", "Log stream only; never send prompts")
311
+ .action(async (id, options) => {
312
+ try {
313
+ const { readBackgroundAgentState, effectiveBackgroundAgentState } =
314
+ await loadSupervisor();
315
+ const state = effectiveBackgroundAgentState(
316
+ readBackgroundAgentState(id),
317
+ );
318
+ if (!state) throw new Error(`Background agent not found: ${id}`);
319
+ const wantsInteractive =
320
+ options.input !== false &&
321
+ options.follow !== false &&
322
+ state.status === "running" &&
323
+ state.transport?.pipe &&
324
+ state.transport?.token &&
325
+ process.stdin.isTTY;
326
+ if (wantsInteractive) {
327
+ const attached = await interactiveAttach(id, state, options);
328
+ if (attached) return;
329
+ // transport refused (worker mid-finalize) — fall back to logs
330
+ }
331
+ await followBackgroundAgent(id, options);
332
+ } catch (error) {
333
+ logger.error(chalk.red(error.message));
334
+ process.exitCode = 1;
335
+ }
336
+ });
337
+
338
+ const daemon = program
339
+ .command("daemon")
340
+ .description("Inspect and stop background agent supervisor sessions");
341
+
342
+ daemon
343
+ .command("status")
344
+ .description("Show background agent supervisor status")
345
+ .option("--all", "Include completed, failed, stopped and lost sessions")
346
+ .option("--json", "Output as JSON")
347
+ .action(async (options) => {
348
+ try {
349
+ const { backgroundAgentsDir, listBackgroundAgents } =
350
+ await loadSupervisor();
351
+ const sessions = listBackgroundAgents({ all: options.all === true });
352
+ const running = sessions.filter((s) => s.status === "running").length;
353
+ if (options.json) {
354
+ console.log(
355
+ JSON.stringify(
356
+ {
357
+ supervisor: "background-agents",
358
+ dir: backgroundAgentsDir(),
359
+ running,
360
+ total: sessions.length,
361
+ sessions,
362
+ },
363
+ null,
364
+ 2,
365
+ ),
366
+ );
367
+ return;
368
+ }
369
+ logger.log(
370
+ `${chalk.bold("Background agent supervisor")} ${running} running ${sessions.length} shown`,
371
+ );
372
+ logger.log(chalk.gray(` dir ${backgroundAgentsDir()}`));
373
+ printBackgroundAgents(sessions);
374
+ } catch (error) {
375
+ logger.error(chalk.red(error.message));
376
+ process.exitCode = 1;
377
+ }
378
+ });
379
+
380
+ daemon
381
+ .command("view <id>")
382
+ .description("Show a detailed background agent view")
383
+ .option("-n, --lines <n>", "Recent log lines to include", "40")
384
+ .option("--json", "Output as JSON")
385
+ .action(async (id, options) => {
386
+ try {
387
+ const {
388
+ readBackgroundAgentState,
389
+ effectiveBackgroundAgentState,
390
+ readBackgroundAgentLog,
391
+ } = await loadSupervisor();
392
+ const session = effectiveBackgroundAgentState(
393
+ readBackgroundAgentState(id),
394
+ );
395
+ if (!session) throw new Error(`Background agent not found: ${id}`);
396
+ const lines = parseLines(options.lines, 40);
397
+ const log = readBackgroundAgentLog(id, { lines });
398
+ if (options.json) {
399
+ console.log(JSON.stringify({ session, log }, null, 2));
400
+ return;
401
+ }
402
+ logger.log(formatBackgroundAgentDetails(session, log));
403
+ } catch (error) {
404
+ logger.error(chalk.red(error.message));
405
+ process.exitCode = 1;
406
+ }
407
+ });
408
+
409
+ daemon
410
+ .command("rename <id> <title...>")
411
+ .description("Rename a background agent session")
412
+ .option("--json", "Output as JSON")
413
+ .action(async (id, title, options) => {
414
+ try {
415
+ const { renameBackgroundAgent } = await loadSupervisor();
416
+ const state = renameBackgroundAgent(
417
+ id,
418
+ Array.isArray(title) ? title.join(" ") : title,
419
+ );
420
+ if (options.json) {
421
+ console.log(JSON.stringify(state, null, 2));
422
+ return;
423
+ }
424
+ logger.log(chalk.green(`Renamed background agent ${state.id}`));
425
+ logger.log(chalk.gray(` ${state.title}`));
426
+ } catch (error) {
427
+ logger.error(chalk.red(error.message));
428
+ process.exitCode = 1;
429
+ }
430
+ });
431
+
432
+ daemon
433
+ .command("resume <id> <prompt...>")
434
+ .description(
435
+ "Continue a finished/crashed background session as a new background run on the same conversation",
436
+ )
437
+ .option("--json", "Output as JSON")
438
+ .action(async (id, prompt, options) => {
439
+ try {
440
+ const { resumeBackgroundAgent } = await loadSupervisor();
441
+ const state = resumeBackgroundAgent(
442
+ id,
443
+ Array.isArray(prompt) ? prompt.join(" ") : prompt,
444
+ );
445
+ if (options.json) {
446
+ console.log(JSON.stringify(state, null, 2));
447
+ return;
448
+ }
449
+ logger.log(
450
+ chalk.green(`Resumed session as background agent ${state.id}`),
451
+ );
452
+ logger.log(chalk.gray(` session ${state.sessionId}`));
453
+ logger.log(chalk.gray(` attach: cc attach ${state.id}`));
454
+ } catch (error) {
455
+ logger.error(chalk.red(error.message));
456
+ process.exitCode = 1;
457
+ }
458
+ });
459
+
460
+ daemon
461
+ .command("stop [id]")
462
+ .description("Stop one background agent, or all running agents with --any")
463
+ .option("--any", "Stop all running background agents")
464
+ .option("--json", "Output as JSON")
465
+ .action(async (id, options) => {
466
+ try {
467
+ const { listBackgroundAgents, stopBackgroundAgent } =
468
+ await loadSupervisor();
469
+ if (!id && options.any !== true) {
470
+ throw new Error("daemon stop requires <id> or --any");
471
+ }
472
+ const targets = options.any
473
+ ? listBackgroundAgents().map((s) => s.id)
474
+ : [id];
475
+ const results = [];
476
+ for (const target of targets) {
477
+ results.push(stopBackgroundAgent(target));
478
+ }
479
+ if (options.json) {
480
+ console.log(JSON.stringify(results, null, 2));
481
+ return;
482
+ }
483
+ if (results.length === 0) {
484
+ logger.log(chalk.gray("No running background agents."));
485
+ return;
486
+ }
487
+ for (const result of results) {
488
+ if (result.stopped) {
489
+ logger.log(chalk.green(`Stopped background agent ${result.id}`));
490
+ } else {
491
+ logger.log(chalk.gray(`${result.id} is already ${result.status}`));
492
+ }
493
+ }
494
+ } catch (error) {
495
+ logger.error(chalk.red(error.message));
496
+ process.exitCode = 1;
497
+ }
498
+ });
499
+ }
@@ -103,7 +103,10 @@ export function registerCommandCommand(program) {
103
103
  .option("--no-bang", "Do not execute !`cmd` bang segments")
104
104
  .option("--output-format <fmt>", "text | json | stream-json", "text")
105
105
  .option("--model <model>", "Override the model")
106
- .option("--permission-mode <mode>", "ApprovalGate tier (see cc agent)")
106
+ .option(
107
+ "--permission-mode <mode>",
108
+ "manual | auto | dontAsk | default | plan | acceptEdits | bypassPermissions",
109
+ )
107
110
  .action(async (name, args, options) => {
108
111
  try {
109
112
  const { getCommand, expandCommand } =
@@ -0,0 +1,147 @@
1
+ /**
2
+ * `cc complete` — inline code completion (fill-in-the-middle) for IDE ghost-text.
3
+ *
4
+ * Reads a JSON request `{prefix, suffix, language, path}` on stdin and prints the
5
+ * raw code to insert at the cursor (or `--json` for a structured reply). The IDE
6
+ * side (VS Code InlineCompletionItemProvider / JetBrains InlineCompletionProvider)
7
+ * builds the request from the editor context and renders the reply as a ghost
8
+ * suggestion. Backend reuse: the SAME provider routing + config as `cc ask`
9
+ * (queryLLM), so it honors the user's configured LLM/provider/key with no new auth.
10
+ *
11
+ * Manual-trigger v1 (the IDE binds it to a keystroke), so a slow/expensive chat
12
+ * model is acceptable; a fast local FIM model (e.g. ollama qwen2.5-coder) just
13
+ * makes it snappier.
14
+ */
15
+ import { loadConfig } from "../lib/config-manager.js";
16
+ import { resolveOllamaBaseUrl, queryLLM } from "./ask.js";
17
+
18
+ /** Hard cap so a runaway model can't flood the editor with a whole file. */
19
+ export const MAX_COMPLETION_CHARS = 2000;
20
+
21
+ /**
22
+ * Build the chat prompt that asks the model to fill in code at the cursor.
23
+ * The cursor is marked with a sentinel between prefix and suffix so a plain
24
+ * chat model (no native FIM) still has the full bidirectional context.
25
+ */
26
+ export function buildFimPrompt(prefix, suffix, language) {
27
+ const lang = (language || "").trim() || "the given language";
28
+ const before = prefix == null ? "" : String(prefix);
29
+ const after = suffix == null ? "" : String(suffix);
30
+ return [
31
+ `You are an inline code-completion engine for ${lang}.`,
32
+ "Continue the code at the position marked <CURSOR>.",
33
+ "Output ONLY the raw code that should be inserted at the cursor —",
34
+ "no explanations, no markdown code fences, and do not repeat the",
35
+ "surrounding code. If nothing should be inserted, output nothing.",
36
+ "",
37
+ `${before}<CURSOR>${after}`,
38
+ ].join("\n");
39
+ }
40
+
41
+ /**
42
+ * Turn the model's raw reply into an insertion string: strip an accidental
43
+ * markdown fence, drop any echoed <CURSOR> sentinel, and hard-cap the length.
44
+ *
45
+ * We deliberately do NOT trim a suffix overlap here — a naive "completion ends
46
+ * with the suffix's first line" cut eats legitimate trailing tokens (e.g. an
47
+ * `if`-block's own `}`), so duplication is left to the prompt's "do not repeat
48
+ * the surrounding code" instruction instead.
49
+ */
50
+ export function cleanCompletion(raw) {
51
+ if (!raw) return "";
52
+ let s = String(raw);
53
+ // A model that ignored "no fences" wraps the code in ```lang … ```.
54
+ s = s.replace(/^\s*```[^\n]*\n?/, "").replace(/\n?```\s*$/, "");
55
+ s = s.replace(/<CURSOR>/g, "");
56
+ if (s.length > MAX_COMPLETION_CHARS) s = s.slice(0, MAX_COMPLETION_CHARS);
57
+ // Trim trailing whitespace only — leading indentation is meaningful.
58
+ return s.replace(/\s+$/, "");
59
+ }
60
+
61
+ /** Read a JSON request from stdin (empty string when nothing is piped in). */
62
+ function readStdin() {
63
+ return new Promise((resolve) => {
64
+ if (process.stdin.isTTY) {
65
+ resolve("");
66
+ return;
67
+ }
68
+ let data = "";
69
+ process.stdin.setEncoding("utf8");
70
+ process.stdin.on("data", (c) => (data += c));
71
+ process.stdin.on("end", () => resolve(data));
72
+ process.stdin.on("error", () => resolve(data));
73
+ });
74
+ }
75
+
76
+ export function registerCompleteCommand(program) {
77
+ program
78
+ .command("complete")
79
+ .description(
80
+ "Inline code completion (FIM) for IDE ghost-text: reads {prefix,suffix,language} JSON on stdin, prints the code to insert.",
81
+ )
82
+ .option("--provider <provider>", "override the LLM provider")
83
+ .option("--model <model>", "override the model")
84
+ .option("--base-url <url>", "override the provider base URL")
85
+ .option("--api-key <key>", "override the API key")
86
+ .option("--json", "output {completion,model,provider} as JSON")
87
+ .action(async (options) => {
88
+ const config = await loadConfig();
89
+ const rawIn = await readStdin();
90
+ let req = {};
91
+ try {
92
+ req = rawIn.trim() ? JSON.parse(rawIn) : {};
93
+ } catch {
94
+ req = {};
95
+ }
96
+ const prefix = typeof req.prefix === "string" ? req.prefix : "";
97
+ const suffix = typeof req.suffix === "string" ? req.suffix : "";
98
+ const language = req.language || "";
99
+
100
+ const emit = (completion, extra = {}) => {
101
+ if (options.json) {
102
+ process.stdout.write(JSON.stringify({ completion, ...extra }));
103
+ } else {
104
+ process.stdout.write(completion);
105
+ }
106
+ };
107
+
108
+ if (!prefix && !suffix) {
109
+ emit(""); // nothing to complete around
110
+ return;
111
+ }
112
+
113
+ const provider = options.provider || config.llm?.provider || "ollama";
114
+ const resolvedOptions = {
115
+ provider,
116
+ model:
117
+ options.model ||
118
+ config.llm?.model ||
119
+ (provider === "ollama" ? "qwen2.5-coder" : undefined),
120
+ apiKey: options.apiKey || config.llm?.apiKey,
121
+ };
122
+ resolvedOptions.baseUrl =
123
+ provider === "ollama"
124
+ ? resolveOllamaBaseUrl({ options, config })
125
+ : options.baseUrl || config.llm?.baseUrl;
126
+
127
+ try {
128
+ const reply = await queryLLM(
129
+ buildFimPrompt(prefix, suffix, language),
130
+ resolvedOptions,
131
+ );
132
+ emit(cleanCompletion(reply), {
133
+ model: resolvedOptions.model,
134
+ provider,
135
+ });
136
+ } catch (e) {
137
+ // Ghost-text must fail quiet: an empty suggestion, not an error dump in
138
+ // the editor. --json carries the reason for debugging.
139
+ if (options.json) {
140
+ emit("", { error: String(e?.message || e) });
141
+ } else {
142
+ process.stderr.write(String(e?.message || e) + "\n");
143
+ process.exitCode = 1;
144
+ }
145
+ }
146
+ });
147
+ }