chainlesschain 0.162.47 → 0.162.49

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 (170) hide show
  1. package/package.json +2 -2
  2. package/src/assets/web-panel/assets/{AIOps-BBjFOl4N.js → AIOps-DmdtNmWd.js} +1 -1
  3. package/src/assets/web-panel/assets/{ActionButton-B7A6lHz3.js → ActionButton-DkuYVafg.js} +1 -1
  4. package/src/assets/web-panel/assets/{Analytics-DZgNxdvw.js → Analytics-Ba_h8Tub.js} +3 -3
  5. package/src/assets/web-panel/assets/{AppLayout-CFbPWVnh.js → AppLayout-yb8Zm9MX.js} +4 -4
  6. package/src/assets/web-panel/assets/{Audit-Ao9RxevG.js → Audit-BGjex2fm.js} +1 -1
  7. package/src/assets/web-panel/assets/{Backup-B1AGqahI.js → Backup-IFQ2hOF2.js} +1 -1
  8. package/src/assets/web-panel/assets/{BaseInput-M01bA2JM.js → BaseInput-W8AkPkrV.js} +1 -1
  9. package/src/assets/web-panel/assets/{Chat-CC0Gmd9k.js → Chat-BgI7t-iW.js} +6 -6
  10. package/src/assets/web-panel/assets/ChatBubbleRenderer-CfpKEQUF.js +1 -0
  11. package/src/assets/web-panel/assets/{Checkbox-CpEqAg5P.js → Checkbox-DuWsZP4g.js} +1 -1
  12. package/src/assets/web-panel/assets/{Codegen-9c1H2P_L.js → Codegen-DyoTNmYg.js} +1 -1
  13. package/src/assets/web-panel/assets/{Col-sH2Obo8q.js → Col-DttmlDRk.js} +1 -1
  14. package/src/assets/web-panel/assets/{Community-DyzjH37r.js → Community-D9nnIdKn.js} +1 -1
  15. package/src/assets/web-panel/assets/{Compact-DsNatLOG.js → Compact-C8KVQaHb.js} +1 -1
  16. package/src/assets/web-panel/assets/{Compliance-B3ws7qwL.js → Compliance-R2owqgjj.js} +1 -1
  17. package/src/assets/web-panel/assets/{Cowork-odGfUMsv.js → Cowork-DwGMMjRn.js} +3 -3
  18. package/src/assets/web-panel/assets/{Cron-bxO6NLDR.js → Cron-BSTcN_lK.js} +2 -2
  19. package/src/assets/web-panel/assets/{Crosschain-xHCPQpE9.js → Crosschain-CTNuIbFD.js} +1 -1
  20. package/src/assets/web-panel/assets/{DID-3l7HvzNC.js → DID-CgApGsFP.js} +2 -2
  21. package/src/assets/web-panel/assets/{Dashboard-BNzT5YBj.js → Dashboard-D_OJ3UN5.js} +2 -2
  22. package/src/assets/web-panel/assets/{Dropdown-DZynMYpG.js → Dropdown-B84Jwra_.js} +1 -1
  23. package/src/assets/web-panel/assets/{EmailListRenderer-6HZ2XA9g.js → EmailListRenderer-Bv-YO-6y.js} +1 -1
  24. package/src/assets/web-panel/assets/{FamilyGuardDashboard-DqzqNzRP.js → FamilyGuardDashboard-drgZ408Y.js} +1 -1
  25. package/src/assets/web-panel/assets/{Federation-CI7lf1MF.js → Federation-CtzFkdW2.js} +1 -1
  26. package/src/assets/web-panel/assets/{FormItemContext-D2m3YZ-k.js → FormItemContext-BFAvNhl9.js} +1 -1
  27. package/src/assets/web-panel/assets/{GenericCardRenderer-Bwcc-9EB.js → GenericCardRenderer-DnuEyz_l.js} +1 -1
  28. package/src/assets/web-panel/assets/{Git-C7qN3bA1.js → Git-jlHajmRJ.js} +2 -2
  29. package/src/assets/web-panel/assets/{Governance-JfuugS1c.js → Governance-DmJC7PGL.js} +1 -1
  30. package/src/assets/web-panel/assets/{Inference-BNsHm2IH.js → Inference-B-u7xD2n.js} +1 -1
  31. package/src/assets/web-panel/assets/{KnowledgeGraph-CTH_FiL1.js → KnowledgeGraph-BaYCA2Cd.js} +1 -1
  32. package/src/assets/web-panel/assets/{Logs-CxcfBcmS.js → Logs-DTNYQWfp.js} +2 -2
  33. package/src/assets/web-panel/assets/{Marketplace-B9PU3qlx.js → Marketplace-CUu1xYvo.js} +1 -1
  34. package/src/assets/web-panel/assets/{McpTools-BlvQpnvj.js → McpTools-BmoeTyrC.js} +5 -5
  35. package/src/assets/web-panel/assets/{Memory-Diiig9gp.js → Memory-DxTU3QU7.js} +2 -2
  36. package/src/assets/web-panel/assets/{MobileBridge-CGfUn4xi.js → MobileBridge-CpcOlKAD.js} +2 -2
  37. package/src/assets/web-panel/assets/MobileProjects-Bjh_z16l.js +1 -0
  38. package/src/assets/web-panel/assets/{Mtc-CT_1x1Gr.js → Mtc-LfxwOm0x.js} +5 -5
  39. package/src/assets/web-panel/assets/{MtcAudit-DPAjU1Qn.js → MtcAudit-D6A9Gjkh.js} +2 -2
  40. package/src/assets/web-panel/assets/{Multisig-nks2HW0C.js → Multisig-Ch_jofPV.js} +3 -3
  41. package/src/assets/web-panel/assets/{NLProgramming-BWd74a91.js → NLProgramming-Bkvogg0I.js} +1 -1
  42. package/src/assets/web-panel/assets/{Notes-B342yG3u.js → Notes-C5t5Xihm.js} +3 -3
  43. package/src/assets/web-panel/assets/{NotificationSettings-C9kjn20B.js → NotificationSettings-CTpDUNCb.js} +1 -1
  44. package/src/assets/web-panel/assets/OrderTableRenderer-ST2lr-Bi.js +1 -0
  45. package/src/assets/web-panel/assets/{Organization-Ch-qkiyP.js → Organization-Dr37BaXa.js} +4 -4
  46. package/src/assets/web-panel/assets/{Overflow-DeV865TY.js → Overflow-ZGjsdP7N.js} +1 -1
  47. package/src/assets/web-panel/assets/{P2P-9pzyAAE1.js → P2P-bWJU5Vxd.js} +2 -2
  48. package/src/assets/web-panel/assets/{PdhVaultBrowser-oGgQiWpR.js → PdhVaultBrowser-BRVoW-ye.js} +3 -3
  49. package/src/assets/web-panel/assets/{Permissions-MPDfvUva.js → Permissions-BOSnFZaC.js} +4 -4
  50. package/src/assets/web-panel/assets/{PersonalDataHub-Es8GFY4_.js → PersonalDataHub-X4SgjP6P.js} +2 -2
  51. package/src/assets/web-panel/assets/{Pipeline-R10lJHtN.js → Pipeline-DoJhB9bj.js} +1 -1
  52. package/src/assets/web-panel/assets/{Privacy-8YMg8xrV.js → Privacy-OM9lDj-R.js} +1 -1
  53. package/src/assets/web-panel/assets/{ProjectInit-B-L3DMBv.js → ProjectInit-BXQEOmLn.js} +2 -2
  54. package/src/assets/web-panel/assets/{ProjectSettings-pR4l7GCe.js → ProjectSettings-DBXo3K5u.js} +2 -2
  55. package/src/assets/web-panel/assets/{Projects-5vaXw-7d.js → Projects-CJ4DBJlS.js} +1 -1
  56. package/src/assets/web-panel/assets/{Providers-C_E-G6Bh.js → Providers-Tk9SawmO.js} +1 -1
  57. package/src/assets/web-panel/assets/{QuickAsk-CM-FSYbK.js → QuickAsk-DRI1-nTC.js} +1 -1
  58. package/src/assets/web-panel/assets/{Recommend-DcwFptAP.js → Recommend-DtrIVTu9.js} +1 -1
  59. package/src/assets/web-panel/assets/{Reputation-DJKj97w-.js → Reputation-DkH8ImwZ.js} +1 -1
  60. package/src/assets/web-panel/assets/{Row-BfI2mGUm.js → Row-DpA9dlvi.js} +1 -1
  61. package/src/assets/web-panel/assets/{RssFeed-D6tzthT0.js → RssFeed-DV3OhxWd.js} +2 -2
  62. package/src/assets/web-panel/assets/{Search-CkxIxA5y.js → Search-QxdntiQx.js} +1 -1
  63. package/src/assets/web-panel/assets/{Security-BeTfFgoX.js → Security-CGuEnrD2.js} +4 -4
  64. package/src/assets/web-panel/assets/{Services-BahX89X1.js → Services-BvwSSD8b.js} +2 -2
  65. package/src/assets/web-panel/assets/{Skeleton-55na3k97.js → Skeleton-sx_8L3-5.js} +1 -1
  66. package/src/assets/web-panel/assets/{Skills-DZ-dxsiR.js → Skills-dWOwxRsu.js} +1 -1
  67. package/src/assets/web-panel/assets/{Sla-BUTClrI0.js → Sla-zxXnfKrT.js} +1 -1
  68. package/src/assets/web-panel/assets/{SpeechSettings-C0c2AmSU.js → SpeechSettings-CmFlcNjr.js} +1 -1
  69. package/src/assets/web-panel/assets/{SyncSettings-Vr5zzdPz.js → SyncSettings-BeXeqURL.js} +2 -2
  70. package/src/assets/web-panel/assets/{Tasks-BrouZA03.js → Tasks-iImd8xSO.js} +1 -1
  71. package/src/assets/web-panel/assets/{Templates-DnaU_7nb.js → Templates-DlgR3XFH.js} +1 -1
  72. package/src/assets/web-panel/assets/{Tenant-1dw4SFBA.js → Tenant-0P8HgQaM.js} +1 -1
  73. package/src/assets/web-panel/assets/Terminal-B5VDEEHD.js +3 -0
  74. package/src/assets/web-panel/assets/{TimelineRenderer-dDxMvAvM.js → TimelineRenderer-hbO7agZs.js} +1 -1
  75. package/src/assets/web-panel/assets/{Tokens-CPJMLjkE.js → Tokens-CsmhgTBO.js} +1 -1
  76. package/src/assets/web-panel/assets/{Trigger-B1IWp6HK.js → Trigger-DnaF_2PP.js} +1 -1
  77. package/src/assets/web-panel/assets/{Trust-iswzFvfA.js → Trust-C1oafGj1.js} +1 -1
  78. package/src/assets/web-panel/assets/{UkeySign-CuMmDUvU.js → UkeySign-eLL4DOmC.js} +1 -1
  79. package/src/assets/web-panel/assets/{VideoEditing-BguHSBQ5.js → VideoEditing-CX45sVq7.js} +1 -1
  80. package/src/assets/web-panel/assets/{Wallet-CW4PFXSw.js → Wallet-aWPqpHdQ.js} +3 -3
  81. package/src/assets/web-panel/assets/{WebAuthn-YOO0lqjJ.js → WebAuthn-DMYV1MAo.js} +5 -5
  82. package/src/assets/web-panel/assets/{WorkflowEditor-A_z3yLuG.js → WorkflowEditor-D9uRIJvH.js} +1 -1
  83. package/src/assets/web-panel/assets/{chat-BSHj9uiJ.js → chat-BmWYfCxG.js} +1 -1
  84. package/src/assets/web-panel/assets/{colors-D7gA-kvN.js → colors-DqvTCkBe.js} +1 -1
  85. package/src/assets/web-panel/assets/{compact-item-ChDt0wUv.js → compact-item-Bh0L0ejI.js} +1 -1
  86. package/src/assets/web-panel/assets/{createContext-5UAhDsNU.js → createContext-r2qgp1mn.js} +1 -1
  87. package/src/assets/web-panel/assets/devWarning-CusWDjWW.js +1 -0
  88. package/src/assets/web-panel/assets/{hasIn-7ysmGHSf.js → hasIn-BcffXa-S.js} +1 -1
  89. package/src/assets/web-panel/assets/{index-Dj3IHF24.js → index-585fuGAN.js} +1 -1
  90. package/src/assets/web-panel/assets/{index-B6OX-6N1.js → index-B3mmDuOv.js} +1 -1
  91. package/src/assets/web-panel/assets/{index-WM_jYoK8.js → index-B6pAm1iJ.js} +1 -1
  92. package/src/assets/web-panel/assets/{index-s_MPIulF.js → index-B7z0qK1W.js} +1 -1
  93. package/src/assets/web-panel/assets/{index-DslmMjVo.js → index-BCBqTRWH.js} +1 -1
  94. package/src/assets/web-panel/assets/{index-HUiKkQOf.js → index-BLN-neIf.js} +1 -1
  95. package/src/assets/web-panel/assets/{index-Dq4fd0cB.js → index-BMn_luHQ.js} +1 -1
  96. package/src/assets/web-panel/assets/{index-NtFFgW-x.js → index-BQlAPNSU.js} +1 -1
  97. package/src/assets/web-panel/assets/{index-BoCy4bxa.js → index-BXXxkeij.js} +1 -1
  98. package/src/assets/web-panel/assets/{index-Db4hf2WV.js → index-BXae4ZyX.js} +1 -1
  99. package/src/assets/web-panel/assets/{index-D9R6sJiP.js → index-BbMox24t.js} +1 -1
  100. package/src/assets/web-panel/assets/{index-B_xBUmAE.js → index-BeV-KoQl.js} +1 -1
  101. package/src/assets/web-panel/assets/index-BhYltBvN.js +1 -0
  102. package/src/assets/web-panel/assets/{index-BGlolJwU.js → index-Bma_yHcC.js} +1 -1
  103. package/src/assets/web-panel/assets/{index-pk9WOpeN.js → index-BtyXyl3t.js} +1 -1
  104. package/src/assets/web-panel/assets/{index-CGVsJplV.js → index-C2-02rrp.js} +1 -1
  105. package/src/assets/web-panel/assets/{index-BzPxVTI2.js → index-C9nh3ANl.js} +1 -1
  106. package/src/assets/web-panel/assets/{index-Cl10KB1R.js → index-CAlxkpnv.js} +1 -1
  107. package/src/assets/web-panel/assets/{index-DSRnnGez.js → index-CCyB-RK5.js} +3 -3
  108. package/src/assets/web-panel/assets/{index-DttrXREN.js → index-CDR3GmaO.js} +1 -1
  109. package/src/assets/web-panel/assets/{index-BhsGGFa4.js → index-CJt0iuep.js} +1 -1
  110. package/src/assets/web-panel/assets/{index--UCy6rsJ.js → index-CKnEtlZD.js} +1 -1
  111. package/src/assets/web-panel/assets/{index-C62SpQws.js → index-COrfHebA.js} +1 -1
  112. package/src/assets/web-panel/assets/{index-D-pJNNc5.js → index-CY8RXaZR.js} +1 -1
  113. package/src/assets/web-panel/assets/index-CZiIHw4e.js +1 -0
  114. package/src/assets/web-panel/assets/{index-CylU_ot3.js → index-Cbj6C3pA.js} +1 -1
  115. package/src/assets/web-panel/assets/{index-C4nXq9eB.js → index-Ct8qhPZe.js} +1 -1
  116. package/src/assets/web-panel/assets/{index-CHdEHlNY.js → index-DPFT7J7I.js} +1 -1
  117. package/src/assets/web-panel/assets/{index-COwKdUAS.js → index-DRXcGa5y.js} +1 -1
  118. package/src/assets/web-panel/assets/{index-BcPEgfGF.js → index-DW1y18GR.js} +1 -1
  119. package/src/assets/web-panel/assets/{index-trVj-RG1.js → index-DWlDSE0F.js} +1 -1
  120. package/src/assets/web-panel/assets/{index-DfjMbFZA.js → index-Dcjol7ot.js} +1 -1
  121. package/src/assets/web-panel/assets/{index-_8xZf-zU.js → index-De36_UgR.js} +1 -1
  122. package/src/assets/web-panel/assets/{index-ZOmjAajS.js → index-DutDlDUF.js} +1 -1
  123. package/src/assets/web-panel/assets/{index-C93TfkpQ.js → index-DxajFkK2.js} +1 -1
  124. package/src/assets/web-panel/assets/{index-QPBJl4wk.js → index-S4E77Aer.js} +1 -1
  125. package/src/assets/web-panel/assets/{index-BLoQ2FkI.js → index-U86pxDyR.js} +1 -1
  126. package/src/assets/web-panel/assets/{index-DcKVERvm.js → index-XwbSqOB2.js} +1 -1
  127. package/src/assets/web-panel/assets/{index-BTzZlYgQ.js → index-h4O0AcBt.js} +1 -1
  128. package/src/assets/web-panel/assets/{initDefaultProps-BcWdcWH2.js → initDefaultProps-C1d8I-BX.js} +1 -1
  129. package/src/assets/web-panel/assets/{motion-D-GC86LC.js → motion-Dq7fiy4Y.js} +1 -1
  130. package/src/assets/web-panel/assets/{move-7N01T4vz.js → move-Bqb2dySM.js} +1 -1
  131. package/src/assets/web-panel/assets/{omit-B8_G8lAi.js → omit-BUYqb4My.js} +1 -1
  132. package/src/assets/web-panel/assets/{pickAttrs-BeEwgLq8.js → pickAttrs-DeytiKlZ.js} +1 -1
  133. package/src/assets/web-panel/assets/{placementArrow-CiQwkkN_.js → placementArrow-xrXZWCqG.js} +1 -1
  134. package/src/assets/web-panel/assets/{responsiveObserve-CtfIP2YM.js → responsiveObserve-CcL2K-YY.js} +1 -1
  135. package/src/assets/web-panel/assets/{slide-DnA-Z3fK.js → slide-DmCWaic7.js} +1 -1
  136. package/src/assets/web-panel/assets/{statusUtils-Ckaf7hg9.js → statusUtils-CqNrFif7.js} +1 -1
  137. package/src/assets/web-panel/assets/{styleChecker-CE7d6-LQ.js → styleChecker-C436m5Xy.js} +1 -1
  138. package/src/assets/web-panel/assets/{useFlexGapSupport-BBC2EqTi.js → useFlexGapSupport-CVhutCN8.js} +1 -1
  139. package/src/assets/web-panel/assets/{useFs-BWncdesy.js → useFs-DUd49Bui.js} +1 -1
  140. package/src/assets/web-panel/assets/{usePersonalDataHub-BVJgKdiT.js → usePersonalDataHub-fuS9raic.js} +1 -1
  141. package/src/assets/web-panel/assets/{vnode-CdZoGwk8.js → vnode-C3kmDmk-.js} +1 -1
  142. package/src/assets/web-panel/assets/{zoom-8BsyiHWV.js → zoom-hX-F1dT-.js} +1 -1
  143. package/src/assets/web-panel/index.html +1 -1
  144. package/src/commands/agent.js +62 -0
  145. package/src/lib/agent-core.js +2 -0
  146. package/src/lib/agent-worktree.js +78 -0
  147. package/src/lib/ide-context.js +168 -0
  148. package/src/lib/mcp-client.js +1 -0
  149. package/src/lib/memory-injection.js +2 -0
  150. package/src/lib/safe-mode.js +31 -0
  151. package/src/lib/settings-hooks.cjs +5 -0
  152. package/src/repl/agent-repl.js +251 -18
  153. package/src/repl/config-summary.js +59 -0
  154. package/src/repl/conversation-export.js +133 -0
  155. package/src/repl/doctor-status.js +114 -0
  156. package/src/repl/ide-status.js +76 -0
  157. package/src/repl/memory-status.js +45 -0
  158. package/src/repl/permissions-status.js +51 -0
  159. package/src/repl/recent-sessions.js +46 -0
  160. package/src/repl/session-cost.js +119 -0
  161. package/src/runtime/agent-core.js +21 -0
  162. package/src/runtime/headless-runner.js +11 -2
  163. package/src/runtime/headless-stream.js +15 -4
  164. package/src/assets/web-panel/assets/ChatBubbleRenderer-DKe4yjqq.js +0 -1
  165. package/src/assets/web-panel/assets/MobileProjects-CFvH6A9E.js +0 -1
  166. package/src/assets/web-panel/assets/OrderTableRenderer-DKQ3FPUN.js +0 -1
  167. package/src/assets/web-panel/assets/Terminal-Pe1vBvOm.js +0 -3
  168. package/src/assets/web-panel/assets/devWarning-NW6VMT5L.js +0 -1
  169. package/src/assets/web-panel/assets/index-A6oY6RQQ.js +0 -1
  170. package/src/assets/web-panel/assets/index-Cn2g4vqt.js +0 -1
@@ -76,6 +76,7 @@ import { composeSystemPrompt } from "../runtime/system-prompt.js";
76
76
  import { makeFallbackChatFn } from "../runtime/fallback-model.js";
77
77
  import { resolveSlashMacro } from "./slash-macro.js";
78
78
  import { expandMcpPrompt, renderMcpSurface } from "./mcp-prompt.js";
79
+ import { newCostStore, addUsage } from "./session-cost.js";
79
80
 
80
81
  /**
81
82
  * Reference to the runtime DB for hook execution (set during startAgentRepl)
@@ -757,21 +758,30 @@ export async function startAgentRepl(options = {}) {
757
758
  "/cd",
758
759
  "/clear",
759
760
  "/compact",
761
+ "/config",
760
762
  "/context",
763
+ "/cost",
761
764
  "/cowork",
765
+ "/doctor",
762
766
  "/exit",
767
+ "/export",
763
768
  "/help",
769
+ "/ide",
764
770
  "/mcp",
771
+ "/memory",
765
772
  "/model",
766
773
  "/output-style",
774
+ "/permissions",
767
775
  "/plan",
768
776
  "/profile",
769
777
  "/provider",
770
778
  "/quit",
771
779
  "/reindex",
780
+ "/reload-skills",
772
781
  "/rewind",
773
782
  "/search",
774
783
  "/session",
784
+ "/sessions",
775
785
  "/stats",
776
786
  "/statusline",
777
787
  "/sub-agents",
@@ -879,6 +889,7 @@ export async function startAgentRepl(options = {}) {
879
889
  let _curModel = model; // tracks the per-turn active model for the readout
880
890
  let _ctxUsedTokens = 0;
881
891
  let _turnCount = 0;
892
+ const _costStore = newCostStore(); // running token spend for `/cost`
882
893
  let _renderStatus = null;
883
894
  try {
884
895
  const slm = await import("../lib/status-line.cjs");
@@ -953,8 +964,16 @@ export async function startAgentRepl(options = {}) {
953
964
  const { runBangCommand } = await import("../lib/repl-bang-memorize.js");
954
965
  const res = runBangCommand(trimmed, { cwd: process.cwd() });
955
966
  logger.log(chalk.gray(`$ ${res.cmd}`));
956
- if (res.stdout) process.stdout.write(res.stdout.endsWith("\n") ? res.stdout : `${res.stdout}\n`);
957
- if (res.stderr) process.stderr.write(chalk.red(res.stderr.endsWith("\n") ? res.stderr : `${res.stderr}\n`));
967
+ if (res.stdout)
968
+ process.stdout.write(
969
+ res.stdout.endsWith("\n") ? res.stdout : `${res.stdout}\n`,
970
+ );
971
+ if (res.stderr)
972
+ process.stderr.write(
973
+ chalk.red(
974
+ res.stderr.endsWith("\n") ? res.stderr : `${res.stderr}\n`,
975
+ ),
976
+ );
958
977
  if (res.error) logger.error(`shell error: ${res.error.message}`);
959
978
  logger.log(chalk.gray(`(exit ${res.exitCode})`));
960
979
  messages.push(res.contextMessage);
@@ -969,14 +988,17 @@ export async function startAgentRepl(options = {}) {
969
988
  // cc.md (auto-loaded next session) and keep it active in this one.
970
989
  if (trimmed.startsWith("#") && trimmed.slice(1).trim()) {
971
990
  try {
972
- const { appendMemoryNote } = await import("../lib/repl-bang-memorize.js");
991
+ const { appendMemoryNote } =
992
+ await import("../lib/repl-bang-memorize.js");
973
993
  const res = appendMemoryNote(trimmed, { cwd: process.cwd() });
974
994
  messages.push({
975
995
  role: "system",
976
996
  content: `<memory-note source="${res.target}">${res.note}</memory-note>`,
977
997
  });
978
998
  logger.log(
979
- chalk.green(`✔ remembered in ${res.target}${res.created ? " (created)" : ""}`),
999
+ chalk.green(
1000
+ `✔ remembered in ${res.target}${res.created ? " (created)" : ""}`,
1001
+ ),
980
1002
  );
981
1003
  } catch (err) {
982
1004
  logger.error(`# memorize failed: ${err.message}`);
@@ -1009,15 +1031,36 @@ export async function startAgentRepl(options = {}) {
1009
1031
  logger.log(
1010
1032
  ` ${chalk.cyan("/statusline")} Context-usage line on/off (/statusline [on|off])`,
1011
1033
  );
1034
+ logger.log(
1035
+ ` ${chalk.cyan("/config")} Effective config (provider/model, keys masked)`,
1036
+ );
1037
+ logger.log(
1038
+ ` ${chalk.cyan("/doctor")} Session health check (provider/key/IDE/MCP/hooks)`,
1039
+ );
1040
+ logger.log(
1041
+ ` ${chalk.cyan("/memory")} Project-memory files loaded (cc.md hierarchy + rules)`,
1042
+ );
1012
1043
  logger.log(
1013
1044
  ` ${chalk.cyan("/context")} Live context-window usage by role`,
1014
1045
  );
1046
+ logger.log(
1047
+ ` ${chalk.cyan("/cost")} Session token spend + estimated $ (so far)`,
1048
+ );
1049
+ logger.log(
1050
+ ` ${chalk.cyan("/permissions")} Allow/ask/deny rules in effect this session`,
1051
+ );
1052
+ logger.log(
1053
+ ` ${chalk.cyan("/export")} Save this conversation to a Markdown file (/export [path])`,
1054
+ );
1015
1055
  logger.log(
1016
1056
  ` ${chalk.cyan("/rewind")} Rewind conversation to an earlier turn (double-Esc lists)`,
1017
1057
  );
1018
1058
  logger.log(
1019
1059
  ` ${chalk.cyan("/cd <dir>")} Change working directory mid-session (completion/memory follow)`,
1020
1060
  );
1061
+ logger.log(
1062
+ ` ${chalk.cyan("/reload-skills")} Re-scan skill layers without restarting`,
1063
+ );
1021
1064
  logger.log(
1022
1065
  ` ${chalk.cyan("/compact")} Smart compact (importance-based)`,
1023
1066
  );
@@ -1026,6 +1069,9 @@ export async function startAgentRepl(options = {}) {
1026
1069
  );
1027
1070
  logger.log(` ${chalk.cyan("/task clear")} Clear current task`);
1028
1071
  logger.log(` ${chalk.cyan("/session")} Show current session info`);
1072
+ logger.log(
1073
+ ` ${chalk.cyan("/sessions")} List recent resumable sessions (/session resume <id> to switch)`,
1074
+ );
1029
1075
  logger.log(
1030
1076
  ` ${chalk.cyan("/reindex")} Reindex notes for BM25 search`,
1031
1077
  );
@@ -1055,6 +1101,9 @@ export async function startAgentRepl(options = {}) {
1055
1101
  logger.log(
1056
1102
  ` ${chalk.cyan("/sub-agents")} Show active/completed sub-agents`,
1057
1103
  );
1104
+ logger.log(
1105
+ ` ${chalk.cyan("/ide")} IDE bridge status (connected editor, tools, or why not)`,
1106
+ );
1058
1107
  logger.log(chalk.bold("\nCapabilities:"));
1059
1108
  logger.log(" Read, write, and edit files");
1060
1109
  logger.log(" Run shell commands (git, npm, etc.)");
@@ -1278,14 +1327,11 @@ export async function startAgentRepl(options = {}) {
1278
1327
 
1279
1328
  if (trimmed === "/rewind" || trimmed.startsWith("/rewind ")) {
1280
1329
  try {
1281
- const { listUserTurns, rewindToTurn, renderTurnList } = await import(
1282
- "../lib/repl-rewind.js"
1283
- );
1330
+ const { listUserTurns, rewindToTurn, renderTurnList } =
1331
+ await import("../lib/repl-rewind.js");
1284
1332
  const arg = trimmed.slice("/rewind".length).trim();
1285
1333
  if (!arg) {
1286
- logger.log(
1287
- chalk.bold("\nRewind — pick a user turn (newest first):"),
1288
- );
1334
+ logger.log(chalk.bold("\nRewind — pick a user turn (newest first):"));
1289
1335
  logger.log(renderTurnList(listUserTurns(messages)));
1290
1336
  logger.log(
1291
1337
  chalk.gray(
@@ -1320,9 +1366,8 @@ export async function startAgentRepl(options = {}) {
1320
1366
  // window. Reuses the same categorizer + estimator as the archived view.
1321
1367
  try {
1322
1368
  const { categorizeContext } = await import("../commands/context.js");
1323
- const { estimateTokens } = await import(
1324
- "../harness/prompt-compressor.js"
1325
- );
1369
+ const { estimateTokens } =
1370
+ await import("../harness/prompt-compressor.js");
1326
1371
  const { buckets, counts, total } = categorizeContext(
1327
1372
  messages,
1328
1373
  estimateTokens,
@@ -1417,7 +1462,7 @@ export async function startAgentRepl(options = {}) {
1417
1462
  }
1418
1463
 
1419
1464
  // Session info
1420
- if (trimmed.startsWith("/session")) {
1465
+ if (trimmed === "/session" || trimmed.startsWith("/session ")) {
1421
1466
  const sessionArg = trimmed.slice(8).trim();
1422
1467
  if (sessionArg.startsWith("resume ")) {
1423
1468
  const resumeId = sessionArg.slice(7).trim();
@@ -1462,6 +1507,24 @@ export async function startAgentRepl(options = {}) {
1462
1507
  }
1463
1508
 
1464
1509
  // Reindex notes
1510
+ // `/reload-skills` (Claude-Code 2.1.152 parity): re-scan the 6 skill
1511
+ // layers (incl. .claude/skills) without restarting the session.
1512
+ if (trimmed === "/reload-skills") {
1513
+ try {
1514
+ const { reloadSkills } = await import("../runtime/agent-core.js");
1515
+ const n = reloadSkills();
1516
+ logger.log(
1517
+ chalk.green(
1518
+ `✔ skills reloaded — ${n} available (6 layers re-scanned)`,
1519
+ ),
1520
+ );
1521
+ } catch (err) {
1522
+ logger.error(`/reload-skills failed: ${err.message}`);
1523
+ }
1524
+ prompt();
1525
+ return;
1526
+ }
1527
+
1465
1528
  if (trimmed === "/reindex") {
1466
1529
  if (contextEngine) {
1467
1530
  contextEngine.reindexNotes();
@@ -2077,6 +2140,42 @@ export async function startAgentRepl(options = {}) {
2077
2140
  return;
2078
2141
  }
2079
2142
 
2143
+ // `/sessions` — list recent RESUMABLE conversations (read-only; the ids
2144
+ // work with `cc agent --resume <id>`). `/session` shows the current one.
2145
+ if (trimmed === "/sessions" || trimmed === "/sessions ") {
2146
+ try {
2147
+ const { listRecentSessions } = await import("../lib/recent-session.js");
2148
+ const { renderRecentSessions } = await import("./recent-sessions.js");
2149
+ const sessions = listRecentSessions({ db: _hookDb }, { scan: 20 });
2150
+ logger.log(renderRecentSessions(sessions, { currentId: sessionId }));
2151
+ } catch (err) {
2152
+ logger.error(chalk.red(`/sessions failed: ${err.message}`));
2153
+ }
2154
+ prompt();
2155
+ return;
2156
+ }
2157
+
2158
+ // `/memory` — project-memory files auto-loaded into the system prompt
2159
+ // (cc.md hierarchy + imports + path-scoped rules). Distinct from `#` (add
2160
+ // a note) and `cc memory recall` (scoped store).
2161
+ if (trimmed === "/memory" || trimmed === "/memory ") {
2162
+ try {
2163
+ const { loadProjectInstructions } =
2164
+ await import("../lib/project-instructions.js");
2165
+ const { renderMemoryFiles } = await import("./memory-status.js");
2166
+ const loaded = loadProjectInstructions({ cwd: process.cwd() });
2167
+ logger.log(
2168
+ renderMemoryFiles(loaded, {
2169
+ enabled: process.env.CC_PROJECT_MEMORY !== "0",
2170
+ }),
2171
+ );
2172
+ } catch (err) {
2173
+ logger.error(chalk.red(`/memory failed: ${err.message}`));
2174
+ }
2175
+ prompt();
2176
+ return;
2177
+ }
2178
+
2080
2179
  // `/mcp` — overview of connected MCP servers' resources + prompts.
2081
2180
  if (trimmed === "/mcp" || trimmed === "/mcp ") {
2082
2181
  const mcpClient = _adhocMcp?.mcpClient || _bundleMcpClient;
@@ -2085,6 +2184,131 @@ export async function startAgentRepl(options = {}) {
2085
2184
  return;
2086
2185
  }
2087
2186
 
2187
+ // `/config` — effective configuration (secret-safe): the LLM provider/model
2188
+ // in effect, whether keys are set, web-search backend, config path.
2189
+ if (trimmed === "/config" || trimmed === "/config ") {
2190
+ try {
2191
+ const { loadConfig } = await import("../lib/config-manager.js");
2192
+ const { getConfigPath } = await import("../lib/paths.js");
2193
+ const { renderConfigSummary } = await import("./config-summary.js");
2194
+ logger.log(
2195
+ renderConfigSummary(loadConfig(), {
2196
+ path: getConfigPath(),
2197
+ activeProvider: provider,
2198
+ activeModel: _curModel || model,
2199
+ }),
2200
+ );
2201
+ } catch (err) {
2202
+ logger.error(chalk.red(`/config failed: ${err.message}`));
2203
+ }
2204
+ prompt();
2205
+ return;
2206
+ }
2207
+
2208
+ // `/doctor` — consolidated session-health readout (Claude-Code parity):
2209
+ // provider/key/IDE/MCP/permissions/hooks in one pass-or-warn view.
2210
+ if (trimmed === "/doctor" || trimmed === "/doctor ") {
2211
+ let config = {};
2212
+ try {
2213
+ config = (await import("../lib/config-manager.js")).loadConfig() || {};
2214
+ } catch (_err) {
2215
+ // config read is best-effort; checks degrade gracefully
2216
+ }
2217
+ const { buildDoctorChecks, renderDoctor } =
2218
+ await import("./doctor-status.js");
2219
+ const { ideToolNames } = await import("./ide-status.js");
2220
+ const checks = buildDoctorChecks({
2221
+ config,
2222
+ ideTools: ideToolNames(_adhocMcp),
2223
+ mcpServers: _adhocMcp?.connected,
2224
+ permissionRules: _permissionRules,
2225
+ settingsHooks: _settingsHooks,
2226
+ });
2227
+ logger.log(renderDoctor(checks));
2228
+ prompt();
2229
+ return;
2230
+ }
2231
+
2232
+ // `/export [path]` — dump the live conversation to a Markdown transcript
2233
+ // (Claude-Code parity). Distinct from `cc export` (knowledge base). Captures
2234
+ // exactly what's in context now, persisted or not.
2235
+ if (trimmed === "/export" || trimmed.startsWith("/export ")) {
2236
+ const arg = trimmed.slice("/export".length).trim();
2237
+ try {
2238
+ const { renderConversationMarkdown, defaultExportFilename } =
2239
+ await import("./conversation-export.js");
2240
+ const fs = await import("fs");
2241
+ const path = await import("path");
2242
+ const md = renderConversationMarkdown(messages, {
2243
+ provider,
2244
+ model: _curModel || model,
2245
+ sessionId,
2246
+ exportedAt: new Date().toISOString(),
2247
+ });
2248
+ const file = arg
2249
+ ? path.resolve(process.cwd(), arg)
2250
+ : path.resolve(process.cwd(), defaultExportFilename(new Date()));
2251
+ fs.writeFileSync(file, md, "utf-8");
2252
+ logger.log(
2253
+ chalk.green(`Exported ${messages.length} messages → ${file}`),
2254
+ );
2255
+ } catch (err) {
2256
+ logger.error(chalk.red(`/export failed: ${err.message}`));
2257
+ }
2258
+ prompt();
2259
+ return;
2260
+ }
2261
+
2262
+ // `/permissions` — allow/ask/deny rules in effect this session (Claude-Code
2263
+ // parity): what the agent runs unprompted, asks about, or is blocked from.
2264
+ if (trimmed === "/permissions" || trimmed === "/permissions ") {
2265
+ let files = [];
2266
+ try {
2267
+ const { loadSettings } = await import("../lib/settings-loader.cjs");
2268
+ files = loadSettings({ cwd: process.cwd() }).files || [];
2269
+ } catch (_err) {
2270
+ // source listing is best-effort — still show the live rules
2271
+ }
2272
+ const { renderPermissions } = await import("./permissions-status.js");
2273
+ logger.log(renderPermissions(_permissionRules, { files }));
2274
+ prompt();
2275
+ return;
2276
+ }
2277
+
2278
+ // `/cost` — running token spend + estimated $ for this session (Claude-Code
2279
+ // parity). In-memory accumulation, so it works without session persistence.
2280
+ if (trimmed === "/cost" || trimmed === "/cost ") {
2281
+ let overrides;
2282
+ try {
2283
+ const { loadConfig } = await import("../lib/config-manager.js");
2284
+ overrides = loadConfig()?.llm?.pricing;
2285
+ } catch (_err) {
2286
+ // pricing overrides are optional — fall back to the built-in table
2287
+ }
2288
+ const { renderSessionCost } = await import("./session-cost.js");
2289
+ logger.log(
2290
+ renderSessionCost(_costStore, { pricingOverrides: overrides }),
2291
+ );
2292
+ prompt();
2293
+ return;
2294
+ }
2295
+
2296
+ // `/ide` — IDE bridge connection status (Claude-Code parity): which editor
2297
+ // is connected, its tools, or why discovery came up empty.
2298
+ if (trimmed === "/ide" || trimmed === "/ide ") {
2299
+ let diag = null;
2300
+ try {
2301
+ const { diagnoseIde } = await import("../lib/ide-bridge.js");
2302
+ diag = diagnoseIde({ cwd: process.cwd(), env: process.env });
2303
+ } catch (_err) {
2304
+ // discovery is best-effort — fall back to in-session tools only
2305
+ }
2306
+ const { renderIdeStatus } = await import("./ide-status.js");
2307
+ logger.log(renderIdeStatus(_adhocMcp, diag));
2308
+ prompt();
2309
+ return;
2310
+ }
2311
+
2088
2312
  // User-defined slash-command macros (.claude/commands/*.md), Claude-Code
2089
2313
  // parity. resolveSlashMacro maps a leading /name to a command macro and
2090
2314
  // expands its template; a non-match returns the line unchanged so a literal
@@ -2201,9 +2425,17 @@ export async function startAgentRepl(options = {}) {
2201
2425
  // Ephemeral: persistence stores effectivePrompt, not this snapshot.
2202
2426
  // Best-effort; CC_IDE_CONTEXT=0 disables.
2203
2427
  try {
2204
- const { buildIdePromptContext } = await import("../lib/ide-context.js");
2428
+ const { buildIdePromptContext, expandIdeMentions } =
2429
+ await import("../lib/ide-context.js");
2205
2430
  const ideCtx = await buildIdePromptContext(_adhocMcp);
2206
2431
  if (ideCtx) userContent += `\n\n${ideCtx}`;
2432
+ // Explicit @selection / @diagnostics mentions (Claude-Code parity);
2433
+ // scan the user's original prompt, append the expansion ephemerally.
2434
+ const mentioned = await expandIdeMentions(effectivePrompt, _adhocMcp);
2435
+ for (const w of mentioned.warnings) {
2436
+ logger.info(chalk.yellow(`[@ide] ${w}`));
2437
+ }
2438
+ if (mentioned.block) userContent += `\n\n${mentioned.block}`;
2207
2439
  } catch (_err) {
2208
2440
  // optional polish — never fail the turn over it
2209
2441
  }
@@ -2305,6 +2537,9 @@ export async function startAgentRepl(options = {}) {
2305
2537
  });
2306
2538
  _turnAbort = null;
2307
2539
 
2540
+ // Running spend for `/cost` (in-memory, works without persistence).
2541
+ if (usageEvents?.length) addUsage(_costStore, usageEvents);
2542
+
2308
2543
  if (sessionId && usageEvents?.length) {
2309
2544
  for (const ue of usageEvents) {
2310
2545
  try {
@@ -2433,9 +2668,7 @@ export async function startAgentRepl(options = {}) {
2433
2668
  // Esc interrupt: an aborted turn is normal flow, not an error — the
2434
2669
  // partial conversation stays usable and queued lines still drain.
2435
2670
  if (err?.name === "AbortError" || /abort/i.test(err?.message || "")) {
2436
- logger.log(
2437
- chalk.yellow("⎋ turn interrupted — partial progress kept"),
2438
- );
2671
+ logger.log(chalk.yellow("⎋ turn interrupted — partial progress kept"));
2439
2672
  prompt();
2440
2673
  return;
2441
2674
  }
@@ -0,0 +1,59 @@
1
+ /**
2
+ * `/config` REPL command (Claude-Code parity) — show the effective
3
+ * configuration in a readable, SECRET-SAFE form: the LLM provider/model/baseURL
4
+ * actually in effect, whether an API key is set (never the key itself), the
5
+ * web-search backend, and the config-file path. Also surfaces the session's
6
+ * active provider/model, which can differ from the file (a --provider flag or
7
+ * .claude/settings.json overrides it).
8
+ *
9
+ * Pure: takes the loaded config + context, returns plain text. The REPL does
10
+ * the I/O. NEVER prints a secret — only "set (…1234)" / "not set".
11
+ */
12
+
13
+ /** Mask a secret: presence + last 4 chars only, or "not set". */
14
+ export function maskSecret(v) {
15
+ if (v == null || v === "") return "not set";
16
+ const s = String(v);
17
+ return s.length <= 4 ? "set (hidden)" : `set (…${s.slice(-4)})`;
18
+ }
19
+
20
+ /**
21
+ * @param {object|null} config loaded config.json
22
+ * @param {object} [opts] { path, activeProvider, activeModel }
23
+ * @returns {string}
24
+ */
25
+ export function renderConfigSummary(config, opts = {}) {
26
+ const cfg = config || {};
27
+ const llm = cfg.llm || {};
28
+ const lines = ["Effective configuration:"];
29
+ if (opts.path) lines.push(` config file: ${opts.path}`);
30
+
31
+ lines.push(" llm:");
32
+ lines.push(` provider: ${llm.provider || "(unset → defaults to ollama)"}`);
33
+ lines.push(` model: ${llm.model || "(unset)"}`);
34
+ if (llm.visionModel) lines.push(` vision: ${llm.visionModel}`);
35
+ if (llm.baseUrl) lines.push(` baseUrl: ${llm.baseUrl}`);
36
+ lines.push(` apiKey: ${maskSecret(llm.apiKey)}`);
37
+
38
+ const ws = cfg.webSearch || {};
39
+ if (ws.provider || ws.apiKey) {
40
+ lines.push(" webSearch:");
41
+ lines.push(` provider: ${ws.provider || "(unset → auto)"}`);
42
+ lines.push(` apiKey: ${maskSecret(ws.apiKey)}`);
43
+ }
44
+
45
+ if (opts.activeProvider || opts.activeModel) {
46
+ const ap = opts.activeProvider || "?";
47
+ const am = opts.activeModel || "?";
48
+ const differs = !!llm.provider && (ap !== llm.provider || am !== llm.model);
49
+ lines.push(
50
+ ` active this session: ${ap} · ${am}` +
51
+ (differs ? " (overrides config)" : ""),
52
+ );
53
+ }
54
+
55
+ lines.push(
56
+ " note: keys are hidden; env vars (e.g. *_API_KEY) can override config at runtime.",
57
+ );
58
+ return lines.join("\n");
59
+ }
@@ -0,0 +1,133 @@
1
+ /**
2
+ * `/export` REPL command — dump the LIVE in-memory conversation to a Markdown
3
+ * transcript (Claude-Code parity). Distinct from `cc export` (knowledge-base
4
+ * export) and from `cc session export` (which reads the persisted JSONL store):
5
+ * this renders the agent REPL's working `messages` array, so it captures
6
+ * exactly what's in context right now, persisted or not.
7
+ *
8
+ * Pure over the OpenAI-shaped message list the agent loop maintains:
9
+ * {role:"user"|"assistant"|"system", content} content: string | parts[]
10
+ * {role:"assistant", content, tool_calls:[{function:{name,arguments}}]}
11
+ * {role:"tool", content, tool_call_id}
12
+ * The REPL does the file I/O; this only produces text + a default filename.
13
+ */
14
+
15
+ export const TOOL_BLOCK_CAP = 4000;
16
+
17
+ /** Render message content (string, or OpenAI multimodal parts[]) as text. */
18
+ function contentToText(content) {
19
+ if (content == null) return "";
20
+ if (typeof content === "string") return content;
21
+ if (Array.isArray(content)) {
22
+ return content
23
+ .map((part) => {
24
+ if (typeof part === "string") return part;
25
+ if (!part || typeof part !== "object") return "";
26
+ if (part.type === "text" || typeof part.text === "string") {
27
+ return part.text || "";
28
+ }
29
+ if (part.type === "image_url" || part.image_url) return "[image]";
30
+ return "";
31
+ })
32
+ .filter(Boolean)
33
+ .join("\n");
34
+ }
35
+ try {
36
+ return JSON.stringify(content, null, 2);
37
+ } catch {
38
+ return String(content);
39
+ }
40
+ }
41
+
42
+ function fence(body, lang = "") {
43
+ const text =
44
+ typeof body === "string"
45
+ ? body
46
+ : (() => {
47
+ try {
48
+ return JSON.stringify(body, null, 2);
49
+ } catch {
50
+ return String(body);
51
+ }
52
+ })();
53
+ const capped =
54
+ text.length > TOOL_BLOCK_CAP
55
+ ? `${text.slice(0, TOOL_BLOCK_CAP)}\n… [truncated]`
56
+ : text;
57
+ const guard = capped.includes("```") ? "````" : "```";
58
+ return `${guard}${lang}\n${capped}\n${guard}`;
59
+ }
60
+
61
+ /** Pretty-print a tool_call's JSON-string arguments, falling back to raw. */
62
+ function prettyArgs(argStr) {
63
+ if (typeof argStr !== "string") return fence(argStr, "json");
64
+ try {
65
+ return fence(JSON.parse(argStr), "json");
66
+ } catch {
67
+ return fence(argStr);
68
+ }
69
+ }
70
+
71
+ const two = (n) => String(n).padStart(2, "0");
72
+
73
+ /**
74
+ * A timestamped default filename, e.g. chainlesschain-export-20260613-130600.md.
75
+ * Takes a Date so callers/tests stay deterministic.
76
+ */
77
+ export function defaultExportFilename(date = new Date()) {
78
+ const d = date instanceof Date && !isNaN(date) ? date : new Date(0);
79
+ const stamp =
80
+ `${d.getFullYear()}${two(d.getMonth() + 1)}${two(d.getDate())}` +
81
+ `-${two(d.getHours())}${two(d.getMinutes())}${two(d.getSeconds())}`;
82
+ return `chainlesschain-export-${stamp}.md`;
83
+ }
84
+
85
+ /**
86
+ * Render the conversation as Markdown.
87
+ * @param {Array} messages the REPL's working message list
88
+ * @param {object} [meta] { provider, model, sessionId, exportedAt }
89
+ * @returns {string}
90
+ */
91
+ export function renderConversationMarkdown(messages, meta = {}) {
92
+ const msgs = Array.isArray(messages) ? messages : [];
93
+ const L = ["# ChainlessChain Conversation Export", ""];
94
+ const bits = [];
95
+ if (meta.sessionId) bits.push(`session: ${meta.sessionId}`);
96
+ if (meta.provider) bits.push(`provider: ${meta.provider}`);
97
+ if (meta.model) bits.push(`model: ${meta.model}`);
98
+ if (meta.exportedAt) bits.push(`exported: ${meta.exportedAt}`);
99
+ if (bits.length) {
100
+ L.push(`> ${bits.join(" · ")}`, "");
101
+ }
102
+
103
+ let users = 0;
104
+ let assistants = 0;
105
+ for (const m of msgs) {
106
+ if (!m || typeof m !== "object") continue;
107
+ const role = m.role;
108
+ if (role === "user") {
109
+ users += 1;
110
+ L.push("## 👤 User", "", contentToText(m.content), "");
111
+ } else if (role === "assistant") {
112
+ const text = contentToText(m.content);
113
+ if (text) {
114
+ assistants += 1;
115
+ L.push("## 🤖 Assistant", "", text, "");
116
+ }
117
+ if (Array.isArray(m.tool_calls)) {
118
+ for (const tc of m.tool_calls) {
119
+ const name = tc?.function?.name || tc?.name || "?";
120
+ L.push(`**🔧 tool_call — \`${name}\`**`, "");
121
+ L.push(prettyArgs(tc?.function?.arguments ?? tc?.arguments), "");
122
+ }
123
+ }
124
+ } else if (role === "tool") {
125
+ L.push("**↩ tool_result**", "", fence(contentToText(m.content)), "");
126
+ } else if (role === "system") {
127
+ L.push("## ⚙ System", "", contentToText(m.content), "");
128
+ }
129
+ }
130
+
131
+ L.push("---", `_${users} user / ${assistants} assistant turns_`, "");
132
+ return L.join("\n");
133
+ }