chainlesschain 0.162.143 → 0.162.148

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 (212) hide show
  1. package/README.md +1 -1
  2. package/package.json +3 -3
  3. package/src/assets/web-panel/.build-hash +1 -1
  4. package/src/assets/web-panel/assets/{AIOps-Bq1t-B4V.js → AIOps-DwpCCp64.js} +1 -1
  5. package/src/assets/web-panel/assets/{ActionButton-BHb3KMc3.js → ActionButton-C9pBYocA.js} +1 -1
  6. package/src/assets/web-panel/assets/{Analytics-DtJrE99O.js → Analytics-BWAkeXxK.js} +2 -2
  7. package/src/assets/web-panel/assets/{AppLayout-HXt309zh.js → AppLayout-CivFGH6B.js} +3 -3
  8. package/src/assets/web-panel/assets/{Audit-B-0384hX.js → Audit-Cm8hRpZm.js} +1 -1
  9. package/src/assets/web-panel/assets/{Backup-BXNwUJ7y.js → Backup-DXdFMsE8.js} +1 -1
  10. package/src/assets/web-panel/assets/{BaseInput-CYdSBe24.js → BaseInput-uAwSTeql.js} +1 -1
  11. package/src/assets/web-panel/assets/{Chat-C5hSt-bf.js → Chat-KU8eeJJE.js} +5 -5
  12. package/src/assets/web-panel/assets/ChatBubbleRenderer-DCNfbdlx.js +1 -0
  13. package/src/assets/web-panel/assets/{Checkbox-DZSpGJs8.js → Checkbox-C6AMDkjd.js} +1 -1
  14. package/src/assets/web-panel/assets/{Codegen-BKfMmpq3.js → Codegen-t2moDxcO.js} +1 -1
  15. package/src/assets/web-panel/assets/{Col-DgsxR9Gb.js → Col-DCcsRRhN.js} +1 -1
  16. package/src/assets/web-panel/assets/{Community-B3GhKOp_.js → Community-DtB-8SAC.js} +1 -1
  17. package/src/assets/web-panel/assets/{Compact-Dtk1xHSY.js → Compact-CZm8THYA.js} +1 -1
  18. package/src/assets/web-panel/assets/{Compliance-DmkkUDze.js → Compliance-LiQgC7g1.js} +1 -1
  19. package/src/assets/web-panel/assets/{Cowork-CB_Tby_i.js → Cowork-CA8SAxht.js} +2 -2
  20. package/src/assets/web-panel/assets/{Cron-BLQFn3UU.js → Cron-CtRaF1wD.js} +2 -2
  21. package/src/assets/web-panel/assets/{Crosschain-LCVSqtiL.js → Crosschain-6-br6Hub.js} +1 -1
  22. package/src/assets/web-panel/assets/{DID-Cv3xor-m.js → DID-Do2EccrL.js} +2 -2
  23. package/src/assets/web-panel/assets/{Dashboard-DHAEYeUk.js → Dashboard-wrXcbzGe.js} +2 -2
  24. package/src/assets/web-panel/assets/{Dropdown-FAp99VHO.js → Dropdown-p3FsT245.js} +1 -1
  25. package/src/assets/web-panel/assets/{EmailListRenderer-Cl4enKIT.js → EmailListRenderer-D95A2L8q.js} +1 -1
  26. package/src/assets/web-panel/assets/{FamilyGuardDashboard-CgbuK_Rv.js → FamilyGuardDashboard-cLuJW2zo.js} +1 -1
  27. package/src/assets/web-panel/assets/{Federation-C2I5-34o.js → Federation-Brgq_cbN.js} +1 -1
  28. package/src/assets/web-panel/assets/{FormItemContext-4RNlhf3D.js → FormItemContext-DWLCkNgh.js} +1 -1
  29. package/src/assets/web-panel/assets/{GenericCardRenderer-DmaKYzyo.js → GenericCardRenderer-BBqUfQd6.js} +1 -1
  30. package/src/assets/web-panel/assets/{Git-BMrCOEGV.js → Git-8F090w4I.js} +2 -2
  31. package/src/assets/web-panel/assets/{Governance-BLwFR0Vd.js → Governance-CCogEbxb.js} +1 -1
  32. package/src/assets/web-panel/assets/{Inference-Cdlx3Y4t.js → Inference-CfLD7v7C.js} +1 -1
  33. package/src/assets/web-panel/assets/{KnowledgeGraph-CiwGb2h3.js → KnowledgeGraph-DDuHJewd.js} +1 -1
  34. package/src/assets/web-panel/assets/{Logs-C6A4r33C.js → Logs-CE8KSEVC.js} +2 -2
  35. package/src/assets/web-panel/assets/{Marketplace-DdybH2YZ.js → Marketplace-DmwpZmhT.js} +1 -1
  36. package/src/assets/web-panel/assets/{McpTools-pgwwEzwg.js → McpTools-Bf7AazU8.js} +4 -4
  37. package/src/assets/web-panel/assets/{Memory-BQ31MLPN.js → Memory-D0QU_00S.js} +2 -2
  38. package/src/assets/web-panel/assets/MobileBridge-D3Arxfeg.css +1 -0
  39. package/src/assets/web-panel/assets/MobileBridge-yXS9xdhx.js +1 -0
  40. package/src/assets/web-panel/assets/{MobileProjects-CmnCTggB.js → MobileProjects-BuDUoGUP.js} +1 -1
  41. package/src/assets/web-panel/assets/{Mtc-3K6RfaUB.js → Mtc-BYyHy28p.js} +2 -2
  42. package/src/assets/web-panel/assets/{MtcAudit-BN9hXvXq.js → MtcAudit-CK0aqpiZ.js} +4 -4
  43. package/src/assets/web-panel/assets/{Multisig-CRxIT2RV.js → Multisig-mNimKYeb.js} +3 -3
  44. package/src/assets/web-panel/assets/{NLProgramming-CdPvh_dA.js → NLProgramming-AioAJ0YA.js} +1 -1
  45. package/src/assets/web-panel/assets/{Notes-DzQSZ0ps.js → Notes-CVVNCLHE.js} +3 -3
  46. package/src/assets/web-panel/assets/{NotificationSettings-DkqRVvfb.js → NotificationSettings-C4ABj-TK.js} +1 -1
  47. package/src/assets/web-panel/assets/{OrderTableRenderer-ClTA64ji.js → OrderTableRenderer-DIp8Xcbd.js} +1 -1
  48. package/src/assets/web-panel/assets/{Organization-B-Pndhhn.js → Organization-uozl_gWK.js} +4 -4
  49. package/src/assets/web-panel/assets/{Overflow-Dt56UM9b.js → Overflow-BCZOM2hK.js} +1 -1
  50. package/src/assets/web-panel/assets/{OverrideContext-C2r4vyBb.js → OverrideContext-BlgL9440.js} +1 -1
  51. package/src/assets/web-panel/assets/{P2P-JDGQmED0.js → P2P-CArcaiaj.js} +2 -2
  52. package/src/assets/web-panel/assets/PdhVaultBrowser-Sgq2Srr8.js +7 -0
  53. package/src/assets/web-panel/assets/{Permissions-Dnqe1sny.js → Permissions-BYrQrXnH.js} +4 -4
  54. package/src/assets/web-panel/assets/{PersonalDataHub-mTkYbIR-.js → PersonalDataHub-BzHStAbz.js} +2 -2
  55. package/src/assets/web-panel/assets/{Pipeline-BG6J9UZO.js → Pipeline-k7KqxX1M.js} +1 -1
  56. package/src/assets/web-panel/assets/{Privacy-BdEydH6w.js → Privacy-IumTUWqx.js} +1 -1
  57. package/src/assets/web-panel/assets/{ProjectInit-QPxCoUzs.js → ProjectInit-BRyImYTf.js} +2 -2
  58. package/src/assets/web-panel/assets/{ProjectSettings-hU_phubM.js → ProjectSettings-BFIqTBFk.js} +2 -2
  59. package/src/assets/web-panel/assets/{Projects-BBzAEGk4.js → Projects-Dpid9CDg.js} +1 -1
  60. package/src/assets/web-panel/assets/{Providers-DJ_bIDRG.js → Providers-CyyFnUPs.js} +1 -1
  61. package/src/assets/web-panel/assets/QrScannerModal-C82cRx-X.js +1 -0
  62. package/src/assets/web-panel/assets/{MobileBridge-zJpPuf4E.css → QrScannerModal-NwAHNfiJ.css} +1 -1
  63. package/src/assets/web-panel/assets/{QuickAsk-BNmX7Jgh.js → QuickAsk-DGxDF521.js} +1 -1
  64. package/src/assets/web-panel/assets/{Recommend-Bo1I5nAG.js → Recommend-Cns-Am1y.js} +1 -1
  65. package/src/assets/web-panel/assets/RemoteSession-R7OqAIlg.js +4 -0
  66. package/src/assets/web-panel/assets/{Reputation-Bc9mfsh7.js → Reputation-D7mgPIYV.js} +1 -1
  67. package/src/assets/web-panel/assets/{Row-D12ZZx5m.js → Row-B3lEURyd.js} +1 -1
  68. package/src/assets/web-panel/assets/{RssFeed-CXSlmgHl.js → RssFeed-9_UVrpBt.js} +2 -2
  69. package/src/assets/web-panel/assets/{Search-bNXObahl.js → Search-ClZeO80q.js} +1 -1
  70. package/src/assets/web-panel/assets/{Security-Bt7yBkJc.js → Security-BNNJczEp.js} +3 -3
  71. package/src/assets/web-panel/assets/{Services-ECQDydWI.js → Services-C5SjrWUj.js} +2 -2
  72. package/src/assets/web-panel/assets/{Skeleton-BcgzLbTM.js → Skeleton-Ci_obQ-g.js} +1 -1
  73. package/src/assets/web-panel/assets/{Skills-py_xMG8b.js → Skills-DdebN15P.js} +1 -1
  74. package/src/assets/web-panel/assets/{Sla-CzNvjwaW.js → Sla-OinZP2vi.js} +1 -1
  75. package/src/assets/web-panel/assets/{SpeechSettings-S3zMMqbB.js → SpeechSettings-CxzbNF51.js} +1 -1
  76. package/src/assets/web-panel/assets/{SyncSettings-BpDfhFv6.js → SyncSettings-D06N_66b.js} +2 -2
  77. package/src/assets/web-panel/assets/{Tasks-CUn36tpM.js → Tasks-BdEdW0AZ.js} +1 -1
  78. package/src/assets/web-panel/assets/{Templates-yzCxc3lI.js → Templates-F1ctKmPa.js} +1 -1
  79. package/src/assets/web-panel/assets/{Tenant-BTMrYsHv.js → Tenant-CVz1Urot.js} +1 -1
  80. package/src/assets/web-panel/assets/Terminal-BvAK_Zel.js +3 -0
  81. package/src/assets/web-panel/assets/{TimelineRenderer-BwJ4nL9S.js → TimelineRenderer-Doitzjmf.js} +1 -1
  82. package/src/assets/web-panel/assets/{Tokens-B1OwTaT_.js → Tokens-BpyVRpVA.js} +1 -1
  83. package/src/assets/web-panel/assets/{Trigger-BDxi9fib.js → Trigger-nWhWYTbU.js} +1 -1
  84. package/src/assets/web-panel/assets/{Trust-B_5hXqzI.js → Trust-DK_G-wLx.js} +1 -1
  85. package/src/assets/web-panel/assets/{UkeySign-BpIMO611.js → UkeySign-B5yBUojO.js} +1 -1
  86. package/src/assets/web-panel/assets/{VideoEditing-Ds-9lxOW.js → VideoEditing-C5D51qAe.js} +1 -1
  87. package/src/assets/web-panel/assets/{Wallet-CNfFp8if.js → Wallet-CLbL9K7v.js} +4 -4
  88. package/src/assets/web-panel/assets/{WebAuthn-DbCj1-qn.js → WebAuthn-DZUelI3V.js} +4 -4
  89. package/src/assets/web-panel/assets/{WorkflowEditor-DjEeb957.js → WorkflowEditor-8RPxt4KW.js} +1 -1
  90. package/src/assets/web-panel/assets/{chat-BX-27gI4.js → chat-4N4tOA3d.js} +1 -1
  91. package/src/assets/web-panel/assets/{collapseMotion-mxzzcDsg.js → collapseMotion-DVfhbsdP.js} +1 -1
  92. package/src/assets/web-panel/assets/{colors-CfM5FmdA.js → colors-zbbGVrua.js} +1 -1
  93. package/src/assets/web-panel/assets/{compact-item-Cb0Fm64g.js → compact-item-D7iMkbnE.js} +1 -1
  94. package/src/assets/web-panel/assets/{createContext-D99MZ4gz.js → createContext-CBzPJv-w.js} +1 -1
  95. package/src/assets/web-panel/assets/devWarning-C2Z5LRgq.js +1 -0
  96. package/src/assets/web-panel/assets/{echarts-jxgIOFUZ.js → echarts-CL0F2SnX.js} +1 -1
  97. package/src/assets/web-panel/assets/{hasIn-CW7wPkfb.js → hasIn-bHdrQSqZ.js} +1 -1
  98. package/src/assets/web-panel/assets/{icons-DP3uiYxy.js → icons-BsDmGpcT.js} +1 -1
  99. package/src/assets/web-panel/assets/{index-BcCSEGzu.js → index-308gCGh7.js} +1 -1
  100. package/src/assets/web-panel/assets/{index-CXrNkD5j.js → index-B1s0Bz9O.js} +1 -1
  101. package/src/assets/web-panel/assets/{index-CxG5S21O.js → index-B7b1hGry.js} +1 -1
  102. package/src/assets/web-panel/assets/{index-BoCp5SY7.js → index-BBNr77E1.js} +1 -1
  103. package/src/assets/web-panel/assets/{index-Dt-h8idq.js → index-BCr0geV9.js} +1 -1
  104. package/src/assets/web-panel/assets/index-BGp6Yr-Y.js +1 -0
  105. package/src/assets/web-panel/assets/{index-Bf7G5mYl.js → index-BJ4ZGyW0.js} +1 -1
  106. package/src/assets/web-panel/assets/{index-rIKv98lY.js → index-BS4_Mwk6.js} +1 -1
  107. package/src/assets/web-panel/assets/{index-ClSExghA.js → index-BTIjjXry.js} +1 -1
  108. package/src/assets/web-panel/assets/index-BXiw9dQf.js +1 -0
  109. package/src/assets/web-panel/assets/{index-CzwoBX8m.js → index-BZb8F8YG.js} +1 -1
  110. package/src/assets/web-panel/assets/{index-CUENEaPU.js → index-BnBOpqv3.js} +1 -1
  111. package/src/assets/web-panel/assets/{index-DZjN-U7A.js → index-Bqmx24kh.js} +1 -1
  112. package/src/assets/web-panel/assets/{index-ab4T5cPr.js → index-BsKehmmS.js} +1 -1
  113. package/src/assets/web-panel/assets/{index-DDGBasj6.js → index-BtbbdLnf.js} +4 -4
  114. package/src/assets/web-panel/assets/{index-CvFAgMxx.js → index-CPxkoKgA.js} +1 -1
  115. package/src/assets/web-panel/assets/{index-BqOy4TS5.js → index-CQ6NcfWz.js} +1 -1
  116. package/src/assets/web-panel/assets/{index--Y8IzVi2.js → index-CQfu1U78.js} +1 -1
  117. package/src/assets/web-panel/assets/{index-DcRbrdqR.js → index-CahryTX0.js} +1 -1
  118. package/src/assets/web-panel/assets/{index-ChGWrUQv.js → index-CdmMoYN1.js} +1 -1
  119. package/src/assets/web-panel/assets/{index-BTlOIaQj.js → index-Ci009ijp.js} +1 -1
  120. package/src/assets/web-panel/assets/{index-CNhZanpM.js → index-CiG1IZao.js} +1 -1
  121. package/src/assets/web-panel/assets/{index-kRlIAKNw.js → index-CkxK86vf.js} +1 -1
  122. package/src/assets/web-panel/assets/{index-DAd31-6W.js → index-CqdPyWm5.js} +1 -1
  123. package/src/assets/web-panel/assets/{index-kt0S8nXp.js → index-CvRMA9uT.js} +1 -1
  124. package/src/assets/web-panel/assets/{index-CxFhtfoo.js → index-D1YDh7Wr.js} +1 -1
  125. package/src/assets/web-panel/assets/{index-7cahuPKG.js → index-D4XKpj6c.js} +1 -1
  126. package/src/assets/web-panel/assets/{index-Cavk4P2D.js → index-DE9j5YgT.js} +1 -1
  127. package/src/assets/web-panel/assets/{index-DUxnWbi6.js → index-DISWAYce.js} +1 -1
  128. package/src/assets/web-panel/assets/{index-CPmtR1Wr.js → index-DUyjUkY7.js} +1 -1
  129. package/src/assets/web-panel/assets/{index-DFRrP3LL.js → index-DbE5D0WL.js} +1 -1
  130. package/src/assets/web-panel/assets/{index-DAIk483F.js → index-DmqlJed-.js} +1 -1
  131. package/src/assets/web-panel/assets/{index-yBiqcb9Z.js → index-DyoNgbXl.js} +1 -1
  132. package/src/assets/web-panel/assets/{index-DwhDhfZ8.js → index-QFObYeo1.js} +1 -1
  133. package/src/assets/web-panel/assets/{index-B8tI4Hg_.js → index-QNAG4JtR.js} +1 -1
  134. package/src/assets/web-panel/assets/{index-BPXhpO02.js → index-S74FpAIn.js} +1 -1
  135. package/src/assets/web-panel/assets/{index-CaPluhcX.js → index-kmh1TxRa.js} +1 -1
  136. package/src/assets/web-panel/assets/{index-3TDpYBKK.js → index-oDKNgCsP.js} +1 -1
  137. package/src/assets/web-panel/assets/{index-D8U1HFcm.js → index-xYCm6W2h.js} +1 -1
  138. package/src/assets/web-panel/assets/{initDefaultProps-mzQV01we.js → initDefaultProps-BD55yNBt.js} +1 -1
  139. package/src/assets/web-panel/assets/{motion-DgWAtKps.js → motion-B5Bbe77U.js} +1 -1
  140. package/src/assets/web-panel/assets/{move-Dm9NMjIw.js → move-CTvIqHEH.js} +1 -1
  141. package/src/assets/web-panel/assets/{mtc-parser-BpuQIoKH.js → mtc-parser-BkNyPQKB.js} +1 -1
  142. package/src/assets/web-panel/assets/{omit-C507FdFH.js → omit-BaXJXgHA.js} +1 -1
  143. package/src/assets/web-panel/assets/{pickAttrs-BL5eykDR.js → pickAttrs-Bw6-YTxH.js} +1 -1
  144. package/src/assets/web-panel/assets/{placementArrow-uSE1U6yg.js → placementArrow-BLdbkLqJ.js} +1 -1
  145. package/src/assets/web-panel/assets/{responsiveObserve-BVhSz_U1.js → responsiveObserve-IVYkzntU.js} +1 -1
  146. package/src/assets/web-panel/assets/{slide-CWCp5XZc.js → slide-DFMFwwtY.js} +1 -1
  147. package/src/assets/web-panel/assets/{statusUtils-B_rS9tlX.js → statusUtils-CURYRtZ1.js} +1 -1
  148. package/src/assets/web-panel/assets/{styleChecker-DRo4TNq4.js → styleChecker-Cfz63s1r.js} +1 -1
  149. package/src/assets/web-panel/assets/useFlexGapSupport-COJL0KE3.js +1 -0
  150. package/src/assets/web-panel/assets/{useFs-DhsLsCr_.js → useFs-YLLojQQf.js} +1 -1
  151. package/src/assets/web-panel/assets/{useMergedState-Cl2q1fes.js → useMergedState-Dc9sO2d7.js} +1 -1
  152. package/src/assets/web-panel/assets/{usePersonalDataHub-BXI-FLwS.js → usePersonalDataHub-BF_21qUC.js} +1 -1
  153. package/src/assets/web-panel/assets/{useRefs-DD9q4mOT.js → useRefs-CAeFRzPf.js} +1 -1
  154. package/src/assets/web-panel/assets/{useState-BK4hy8Ma.js → useState-LwExUYDO.js} +1 -1
  155. package/src/assets/web-panel/assets/{vendor-BvqAck49.js → vendor-BMxUs6UX.js} +16 -16
  156. package/src/assets/web-panel/assets/{vnode-CYE0CVkI.js → vnode-9ZpkA7bb.js} +1 -1
  157. package/src/assets/web-panel/assets/{zoom-B2-MtXVF.js → zoom-DhtAfhl8.js} +1 -1
  158. package/src/assets/web-panel/index.html +3 -3
  159. package/src/auth/npm-auth.js +65 -0
  160. package/src/commands/agent.js +92 -0
  161. package/src/commands/agents.js +68 -0
  162. package/src/commands/permissions.js +66 -10
  163. package/src/commands/plugin.js +50 -3
  164. package/src/commands/serve.js +10 -0
  165. package/src/gateways/remote-session-relay.js +160 -0
  166. package/src/gateways/ws/message-dispatcher.js +32 -0
  167. package/src/gateways/ws/remote-session-protocol.js +429 -0
  168. package/src/gateways/ws/ws-server.js +299 -0
  169. package/src/harness/golden-transcript.js +65 -0
  170. package/src/harness/mcp-client.js +6 -2
  171. package/src/harness/remote-session-audit-sink.js +132 -0
  172. package/src/harness/remote-session-audit.js +110 -0
  173. package/src/harness/remote-session-crypto.js +217 -0
  174. package/src/harness/remote-session-push-fcm.js +254 -0
  175. package/src/harness/remote-session-push.js +104 -0
  176. package/src/harness/remote-session-registry.js +420 -0
  177. package/src/lib/agent-sandbox.js +72 -0
  178. package/src/lib/background-agent-supervisor.js +197 -0
  179. package/src/lib/code-review.js +72 -0
  180. package/src/lib/did-manager.js +6 -4
  181. package/src/lib/mcp-oauth.js +23 -6
  182. package/src/lib/plugin-security.js +128 -0
  183. package/src/lib/pr-create.js +108 -0
  184. package/src/lib/publish-workspace.js +107 -0
  185. package/src/lib/resume-session.js +111 -0
  186. package/src/lib/session-manager.js +7 -3
  187. package/src/lib/settings-hooks.cjs +68 -25
  188. package/src/lib/settings-loader.cjs +115 -2
  189. package/src/lib/task-list.js +53 -0
  190. package/src/repl/agent-repl.js +83 -2
  191. package/src/repl/agents-status.js +8 -2
  192. package/src/repl/btw-command.js +56 -0
  193. package/src/repl/chat-repl.js +52 -1
  194. package/src/repl/hooks-status.js +1 -2
  195. package/src/repl/slash-command-registry.js +197 -0
  196. package/src/runtime/agent-core.js +137 -1
  197. package/src/runtime/agent-runtime.js +8 -13
  198. package/src/runtime/coding-agent-contract-shared.cjs +37 -0
  199. package/src/runtime/coding-agent-policy.cjs +14 -0
  200. package/src/runtime/headless-runner.js +26 -6
  201. package/src/runtime/headless-stream.js +25 -6
  202. package/src/runtime/mcp-config.js +46 -2
  203. package/src/runtime/policies/agent-policy.js +3 -0
  204. package/src/workers/background-agent-worker.js +60 -0
  205. package/src/assets/web-panel/assets/ChatBubbleRenderer-BJQ6SG49.js +0 -1
  206. package/src/assets/web-panel/assets/MobileBridge-CnRXuSP5.js +0 -1
  207. package/src/assets/web-panel/assets/PdhVaultBrowser-C56Oi811.js +0 -7
  208. package/src/assets/web-panel/assets/Terminal-BjZry1Jj.js +0 -3
  209. package/src/assets/web-panel/assets/devWarning-CDh1y3xt.js +0 -1
  210. package/src/assets/web-panel/assets/index-ACeVMo7x.js +0 -1
  211. package/src/assets/web-panel/assets/index-gYKDzWA9.js +0 -1
  212. package/src/assets/web-panel/assets/useFlexGapSupport-QsPzwpvi.js +0 -1
@@ -82,20 +82,66 @@ function readJson(file, onWarn) {
82
82
  }
83
83
  }
84
84
 
85
+ function managedSettingsFile(env = process.env) {
86
+ if (env.CC_MANAGED_SETTINGS) return path.resolve(env.CC_MANAGED_SETTINGS);
87
+ if (process.platform === "win32") {
88
+ const base = env.ProgramData || env.PROGRAMDATA || "C:\\ProgramData";
89
+ return path.join(base, "ChainlessChain", "managed-settings.json");
90
+ }
91
+ return "/etc/chainlesschain/managed-settings.json";
92
+ }
93
+
94
+ function appendHookBlock(merged, data) {
95
+ const block =
96
+ data && data.hooks && typeof data.hooks === "object" ? data.hooks : null;
97
+ if (!block) return false;
98
+ let contributed = false;
99
+ for (const [event, groups] of Object.entries(block)) {
100
+ if (!Array.isArray(groups)) continue;
101
+ for (const g of groups) {
102
+ if (!g || typeof g !== "object" || !Array.isArray(g.hooks)) continue;
103
+ const cmds = g.hooks.filter(
104
+ (h) => h && h.type === "command" && h.command,
105
+ );
106
+ if (cmds.length === 0) continue;
107
+ if (!merged[event]) merged[event] = [];
108
+ merged[event].push({ matcher: g.matcher ?? null, hooks: cmds });
109
+ contributed = true;
110
+ }
111
+ }
112
+ return contributed;
113
+ }
114
+
85
115
  /**
86
116
  * Load + concatenate the `hooks` blocks across the hierarchy.
87
117
  * @returns {{ hooks: Record<string, Array<{matcher:string|null, hooks:Array<{type,command,timeout}>}>>, files:string[] }}
88
118
  */
89
- function loadHooks({ cwd = process.cwd(), settingsFile, onWarn } = {}) {
119
+ function loadHooks({
120
+ cwd = process.cwd(),
121
+ settingsFile,
122
+ onWarn,
123
+ env = process.env,
124
+ } = {}) {
90
125
  const merged = {};
91
126
  const files = [];
92
- // Safe-mode kill switch (`cc agent --safe-mode` sets CC_SETTINGS_HOOKS=0):
93
- // run with NO settings hooks so a broken hook can be diagnosed.
94
- if (process.env.CC_SETTINGS_HOOKS === "0") {
95
- return { hooks: merged, files };
127
+ const managedFile = managedSettingsFile(env);
128
+ let managed = null;
129
+ if (_deps.fs.existsSync(managedFile)) {
130
+ managed = readJson(managedFile, onWarn);
131
+ if (!managed) {
132
+ const error = new Error(
133
+ `managed settings are unreadable or malformed: ${managedFile}`,
134
+ );
135
+ error.code = "CC_MANAGED_SETTINGS_INVALID";
136
+ throw error;
137
+ }
96
138
  }
139
+ // Safe-mode kill switch (`cc agent --safe-mode` sets CC_SETTINGS_HOOKS=0):
140
+ // user/project hooks are disabled, but administrator hooks remain active.
141
+ const managedOnly = managed?.allowManagedHooksOnly === true;
142
+ const skipUnmanaged = env.CC_SETTINGS_HOOKS === "0" || managedOnly;
97
143
  const seenPaths = new Set();
98
- for (const file of settingsFiles(cwd, settingsFile)) {
144
+ for (const file of skipUnmanaged ? [] : settingsFiles(cwd, settingsFile)) {
99
145
  // Dedup by RESOLVED path so the SAME physical file is never read twice — e.g.
100
146
  // `--settings .claude/settings.json` from the project root aliases the
101
147
  // auto-loaded cwd file, which would otherwise CONCATENATE its hook groups a
@@ -106,26 +152,16 @@ function loadHooks({ cwd = process.cwd(), settingsFile, onWarn } = {}) {
106
152
  if (seenPaths.has(resolved)) continue;
107
153
  seenPaths.add(resolved);
108
154
  const data = readJson(file, onWarn);
109
- const block =
110
- data && data.hooks && typeof data.hooks === "object" ? data.hooks : null;
111
- if (!block) continue;
112
- let contributed = false;
113
- for (const [event, groups] of Object.entries(block)) {
114
- if (!Array.isArray(groups)) continue;
115
- for (const g of groups) {
116
- if (!g || typeof g !== "object" || !Array.isArray(g.hooks)) continue;
117
- const cmds = g.hooks.filter(
118
- (h) => h && h.type === "command" && h.command,
119
- );
120
- if (cmds.length === 0) continue;
121
- if (!merged[event]) merged[event] = [];
122
- merged[event].push({ matcher: g.matcher ?? null, hooks: cmds });
123
- contributed = true;
124
- }
125
- }
155
+ const contributed = appendHookBlock(merged, data);
126
156
  if (contributed) files.push(file);
127
157
  }
128
- return { hooks: merged, files };
158
+ if (managed && appendHookBlock(merged, managed)) files.push(managedFile);
159
+ const result = { hooks: merged, files };
160
+ if (managed) {
161
+ result.managed = managed;
162
+ result.managedFile = managedFile;
163
+ }
164
+ return result;
129
165
  }
130
166
 
131
167
  /**
@@ -184,7 +220,8 @@ function collectHooks(hooksBlock, event, toolName) {
184
220
  for (const g of groups) {
185
221
  const fn = compileMatcher(g.matcher);
186
222
  if (fn(umbrella) || (raw && fn(raw))) {
187
- for (const h of g.hooks) out.push({ command: h.command, timeout: h.timeout });
223
+ for (const h of g.hooks)
224
+ out.push({ command: h.command, timeout: h.timeout });
188
225
  }
189
226
  }
190
227
  return out;
@@ -265,6 +302,11 @@ function writeHookTrustStore(store) {
265
302
  function projectHookTrustNotice({ cwd = process.cwd(), settingsFile } = {}) {
266
303
  if (process.env.CC_HOOK_TRUST_NOTICE === "0") return null;
267
304
  if (process.env.CC_SETTINGS_HOOKS === "0") return null; // hooks won't run anyway
305
+ const managedFile = managedSettingsFile(process.env);
306
+ if (_deps.fs.existsSync(managedFile)) {
307
+ const managed = readJson(managedFile);
308
+ if (managed?.allowManagedHooksOnly === true) return null;
309
+ }
268
310
  const home = path.join(_deps.homedir(), ".claude", "settings.json");
269
311
  const explicit = settingsFile ? path.resolve(cwd, settingsFile) : null;
270
312
  const trusted = new Set([home, explicit].filter(Boolean));
@@ -328,6 +370,7 @@ module.exports = {
328
370
  compileMatcher,
329
371
  umbrellaFor,
330
372
  settingsFiles,
373
+ managedSettingsFile,
331
374
  HOOK_EVENTS,
332
375
  _deps,
333
376
  };
@@ -88,6 +88,69 @@ function settingsPaths(cwd, explicitFile) {
88
88
  return list;
89
89
  }
90
90
 
91
+ /** Organization-controlled settings file. This layer is always highest. */
92
+ function managedSettingsPath(opts = {}) {
93
+ if (opts.managedSettingsFile) return path.resolve(opts.managedSettingsFile);
94
+ const env = opts.env || process.env;
95
+ if (env.CC_MANAGED_SETTINGS) return path.resolve(env.CC_MANAGED_SETTINGS);
96
+ if (process.platform === "win32") {
97
+ const base = env.ProgramData || env.PROGRAMDATA || "C:\\ProgramData";
98
+ return path.join(base, "ChainlessChain", "managed-settings.json");
99
+ }
100
+ return "/etc/chainlesschain/managed-settings.json";
101
+ }
102
+
103
+ function loadManagedSettings(opts = {}) {
104
+ const file = managedSettingsPath(opts);
105
+ if (!_deps.fs.existsSync(file)) return { file, settings: null };
106
+ const data = readSettingsFile(file, { onWarn: opts.onWarn });
107
+ if (!data) {
108
+ const error = new Error(
109
+ `managed settings are unreadable or malformed: ${file}`,
110
+ );
111
+ error.code = "CC_MANAGED_SETTINGS_INVALID";
112
+ throw error;
113
+ }
114
+ return { file, settings: data };
115
+ }
116
+
117
+ function emptyRules() {
118
+ return { allow: [], ask: [], deny: [] };
119
+ }
120
+
121
+ /** Apply the non-bypassable permission portion of managed settings. */
122
+ function applyManagedPermissionPolicy(baseRules, managed, sources = {}) {
123
+ const policy = managed && typeof managed === "object" ? managed : {};
124
+ const rules = policy.allowManagedPermissionRulesOnly
125
+ ? emptyRules()
126
+ : {
127
+ allow: [...(baseRules?.allow || [])],
128
+ ask: [...(baseRules?.ask || [])],
129
+ deny: [...(baseRules?.deny || [])],
130
+ };
131
+ const perms =
132
+ policy.permissions && typeof policy.permissions === "object"
133
+ ? policy.permissions
134
+ : {};
135
+ for (const kind of KINDS) {
136
+ accrete(rules[kind], sources, perms[kind], "<managed>", kind);
137
+ }
138
+ return rules;
139
+ }
140
+
141
+ function assertManagedPermissionMode(mode, managed) {
142
+ if (
143
+ mode === "bypassPermissions" &&
144
+ (managed?.disableBypassPermissionsMode === true ||
145
+ managed?.disableBypassPermissionsMode === "disable")
146
+ ) {
147
+ throw new Error(
148
+ 'permission mode "bypassPermissions" is disabled by managed settings',
149
+ );
150
+ }
151
+ return mode;
152
+ }
153
+
91
154
  /** Parse a comma/space separated env override into a string[]. */
92
155
  function parseEnvList(value) {
93
156
  if (!value) return [];
@@ -116,7 +179,7 @@ function loadSettings(opts = {}) {
116
179
  const env = opts.env || process.env;
117
180
  const onWarn = opts.onWarn;
118
181
 
119
- const rules = { allow: [], ask: [], deny: [] };
182
+ let rules = emptyRules();
120
183
  const sources = {};
121
184
  const files = [];
122
185
 
@@ -150,7 +213,27 @@ function loadSettings(opts = {}) {
150
213
  }
151
214
  if (envContributed) files.push("<env>");
152
215
 
153
- return { rules, sources, files };
216
+ const managedLoaded = loadManagedSettings({
217
+ env,
218
+ managedSettingsFile: opts.managedSettingsFile,
219
+ onWarn,
220
+ });
221
+ if (managedLoaded.settings) {
222
+ rules = applyManagedPermissionPolicy(
223
+ rules,
224
+ managedLoaded.settings,
225
+ sources,
226
+ );
227
+ files.push(managedLoaded.file);
228
+ }
229
+
230
+ return {
231
+ rules,
232
+ sources,
233
+ files,
234
+ managed: managedLoaded.settings,
235
+ managedFile: managedLoaded.settings ? managedLoaded.file : null,
236
+ };
154
237
  }
155
238
 
156
239
  /** Look up the source file a matched `{ kind, rule }` came from. */
@@ -236,6 +319,24 @@ function loadSettingsConfig(opts = {}) {
236
319
  }
237
320
  if (contributed) files.push(file);
238
321
  }
322
+ const managed = loadManagedSettings(opts);
323
+ if (managed.settings) {
324
+ const data = managed.settings;
325
+ let contributed = false;
326
+ if (typeof data.model === "string" && data.model.trim()) {
327
+ model = data.model.trim();
328
+ contributed = true;
329
+ }
330
+ if (data.env && typeof data.env === "object" && !Array.isArray(data.env)) {
331
+ for (const [k, v] of Object.entries(data.env)) {
332
+ if (typeof v === "string") {
333
+ env[k] = v;
334
+ contributed = true;
335
+ }
336
+ }
337
+ }
338
+ if (contributed) files.push(managed.file);
339
+ }
239
340
  return { model, env, files };
240
341
  }
241
342
 
@@ -270,6 +371,14 @@ function readBooleanSetting(key, opts = {}) {
270
371
  value = node; // last (closest) layer wins
271
372
  }
272
373
  }
374
+ const managed = loadManagedSettings(opts);
375
+ if (managed.settings) {
376
+ let node = managed.settings;
377
+ for (const p of parts) {
378
+ node = node && typeof node === "object" ? node[p] : undefined;
379
+ }
380
+ if (typeof node === "boolean") value = node;
381
+ }
273
382
  return value;
274
383
  }
275
384
 
@@ -279,6 +388,10 @@ module.exports = {
279
388
  readSettingsFile,
280
389
  readBooleanSetting,
281
390
  settingsPaths,
391
+ managedSettingsPath,
392
+ loadManagedSettings,
393
+ applyManagedPermissionPolicy,
394
+ assertManagedPermissionMode,
282
395
  parseEnvList,
283
396
  ruleSource,
284
397
  addRule,
@@ -0,0 +1,53 @@
1
+ /**
2
+ * Background task listing utilities (ESM)
3
+ * Mirrors Claude Code /tasks behavior
4
+ * @module lib/task-list
5
+ */
6
+
7
+ import { listBackgroundShellTasks } from "../runtime/agent-core.js";
8
+ import { formatBackgroundTasks } from "../repl/tasks-status.js";
9
+ import { logger } from "./logger.js";
10
+
11
+ /**
12
+ * List all background tasks
13
+ * @param {Object} options List options
14
+ * @returns {Promise<Object>} task list result
15
+ */
16
+ export async function listTasks(options = {}) {
17
+ const tasks = listBackgroundShellTasks();
18
+ const active = tasks.filter((t) => t.status === "running");
19
+ const completed = tasks.filter((t) => t.status !== "running");
20
+
21
+ if (tasks.length === 0) {
22
+ logger.info("No background tasks");
23
+ return { count: 0, active: 0, completed: 0, tasks: [] };
24
+ }
25
+
26
+ if (!options.quiet) {
27
+ logger.info(formatBackgroundTasks(tasks));
28
+ }
29
+
30
+ logger.info(
31
+ ` ${active.length} active, ${completed.length} completed (total: ${tasks.length})`,
32
+ );
33
+
34
+ return {
35
+ count: tasks.length,
36
+ active: active.length,
37
+ completed: completed.length,
38
+ tasks,
39
+ formatted: formatBackgroundTasks(tasks),
40
+ };
41
+ }
42
+
43
+ /**
44
+ * Get a specific task by ID
45
+ * @param {string} taskId
46
+ * @returns {Object|null}
47
+ */
48
+ export function getTask(taskId) {
49
+ const tasks = listBackgroundShellTasks();
50
+ return tasks.find((t) => t.id === taskId) || null;
51
+ }
52
+
53
+ export default { listTasks, getTask };
@@ -98,6 +98,7 @@ import { resolveSlashMacro } from "./slash-macro.js";
98
98
  import { expandMcpPrompt, renderMcpSurface } from "./mcp-prompt.js";
99
99
  import { newCostStore, addUsage } from "./session-cost.js";
100
100
  import { parseThinkCommand, parseEffortCommand } from "./think-command.js";
101
+ import { parseBtwCommand, buildAsideBlock, applyAside } from "./btw-command.js";
101
102
  import { shouldStreamLive } from "./stream-decision.js";
102
103
  import { emptyTurnNotice } from "./empty-turn-notice.js";
103
104
  import { buildPermissionPrompt } from "./permission-prompt.js";
@@ -117,6 +118,7 @@ let _approvalGate = null;
117
118
  // confirmer for `ask` matches. Loaded once at REPL startup; null = no file.
118
119
  let _permissionRules = null;
119
120
  let _permissionConfirm = null;
121
+ let _managedPermissionRulesOnly = false;
120
122
  // .claude/settings.json `hooks` block (decision-capable PreToolUse/PostToolUse).
121
123
  let _settingsHooks = null;
122
124
  // .claude/settings.json `respondToBashCommands` (Claude-Code 2.1.186): whether a
@@ -127,6 +129,7 @@ let _respondToBash;
127
129
  // the built-in verification allowlist through the shell-policy classifier (→
128
130
  // ApprovalGate confirm) instead of fast-pathing it. false = unset → off.
129
131
  let _classifyAllShell = false;
132
+ let _sandbox = null;
130
133
  // Bounded log of tool calls the agent was BLOCKED from running this session
131
134
  // (shell-policy / ApprovalGate / settings rule / hook). Surfaced by
132
135
  // `/permissions denials` (Claude-Code 2.1.193 "recent denials"). In-memory only.
@@ -161,6 +164,12 @@ async function _fireNotification(message) {
161
164
  */
162
165
  async function _persistAlwaysAllow(tool, args) {
163
166
  try {
167
+ if (_managedPermissionRulesOnly) {
168
+ process.stderr.write(
169
+ " always-allow is disabled by managed settings; ask your administrator to change the policy.\n",
170
+ );
171
+ return null;
172
+ }
164
173
  const rulesMod = await import("../lib/permission-rules.cjs");
165
174
  const { suggestAllowRule } = rulesMod.default || rulesMod;
166
175
  const rule = suggestAllowRule(tool || "run_shell", args || {});
@@ -194,6 +203,7 @@ async function executeTool(name, args) {
194
203
  permissionConfirm: _permissionConfirm,
195
204
  settingsHooks: _settingsHooks,
196
205
  classifyAllShell: _classifyAllShell,
206
+ sandbox: _sandbox,
197
207
  });
198
208
  }
199
209
 
@@ -371,6 +381,12 @@ export async function startAgentRepl(options = {}) {
371
381
  // (--thinking-budget) is the companion legacy-model budget_tokens override.
372
382
  let thinking = options.thinking || null;
373
383
  const thinkingBudget = options.thinkingBudget || null;
384
+ // `/btw` one-shot asides: queued notes that ride ONLY the next turn (sent to
385
+ // the model, then stripped so they never persist or carry forward).
386
+ // `_btwRestore` holds the user message + its pre-aside content so we can undo
387
+ // the injection after the turn (or as a backstop at the next submit on throw).
388
+ let pendingBtw = [];
389
+ let _btwRestore = null;
374
390
  // Current ApprovalGate session tier (strict|trusted|autopilot), mirrored here
375
391
  // so Shift+Tab can cycle it and `/permissions <tier>` can set it. Kept in sync
376
392
  // with _approvalGate.setSessionPolicy below.
@@ -402,6 +418,7 @@ export async function startAgentRepl(options = {}) {
402
418
  const additionalDirectories = Array.isArray(options.additionalDirectories)
403
419
  ? options.additionalDirectories
404
420
  : [];
421
+ _sandbox = options.sandbox || null;
405
422
  // Snapshot the work tree before each mutating tool (git engine) so the user
406
423
  // can `cc checkpoint restore` to just before any tool call.
407
424
  const autoCheckpoint = options.autoCheckpoint === true;
@@ -542,6 +559,8 @@ export async function startAgentRepl(options = {}) {
542
559
  const { loadSettings, readBooleanSetting } =
543
560
  await import("../lib/settings-loader.cjs");
544
561
  const loaded = loadSettings({ cwd: process.cwd() });
562
+ _managedPermissionRulesOnly =
563
+ loaded.managed?.allowManagedPermissionRulesOnly === true;
545
564
  // Claude-Code 2.1.186 respondToBashCommands (default OFF / opt-in when unset).
546
565
  _respondToBash = readBooleanSetting("respondToBashCommands", {
547
566
  cwd: process.cwd(),
@@ -578,8 +597,10 @@ export async function startAgentRepl(options = {}) {
578
597
  return ans === "y" || ans === "yes";
579
598
  };
580
599
  } catch (_err) {
600
+ if (_err?.code === "CC_MANAGED_SETTINGS_INVALID") throw _err;
581
601
  _permissionRules = null;
582
602
  _permissionConfirm = null;
603
+ _managedPermissionRulesOnly = false;
583
604
  }
584
605
 
585
606
  // Load .claude/settings.json `hooks` block (decision-capable PreToolUse/
@@ -1040,6 +1061,7 @@ export async function startAgentRepl(options = {}) {
1040
1061
  "/add-dir",
1041
1062
  "/agents",
1042
1063
  "/auto",
1064
+ "/btw",
1043
1065
  "/cd",
1044
1066
  "/clear",
1045
1067
  "/compact",
@@ -1518,6 +1540,9 @@ export async function startAgentRepl(options = {}) {
1518
1540
  logger.log(
1519
1541
  ` ${chalk.cyan("/effort")} Reasoning effort (/effort low|medium|high|xhigh; Anthropic)`,
1520
1542
  );
1543
+ logger.log(
1544
+ ` ${chalk.cyan("/btw")} Queue a one-off aside for your next message (not saved to history)`,
1545
+ );
1521
1546
  logger.log(` ${chalk.cyan("/clear")} Clear conversation`);
1522
1547
  logger.log(
1523
1548
  ` ${chalk.cyan("/vim")} Toggle vim-mode line editing (/vim [on|off]; Esc → NORMAL)`,
@@ -2030,6 +2055,27 @@ export async function startAgentRepl(options = {}) {
2030
2055
  }
2031
2056
  }
2032
2057
 
2058
+ // `/btw <note>` — queue a one-shot aside (Claude-Code parity). It rides the
2059
+ // NEXT message to the model, then is stripped so it never bloats history.
2060
+ {
2061
+ const btw = parseBtwCommand(trimmed);
2062
+ if (btw) {
2063
+ if (btw.error) {
2064
+ logger.info(btw.error);
2065
+ prompt();
2066
+ return;
2067
+ }
2068
+ pendingBtw.push(btw.text);
2069
+ logger.info(
2070
+ chalk.gray(
2071
+ `Aside noted (${pendingBtw.length}) — rides your next message only, not saved to history.`,
2072
+ ),
2073
+ );
2074
+ prompt();
2075
+ return;
2076
+ }
2077
+ }
2078
+
2033
2079
  if (trimmed === "/clear") {
2034
2080
  // Stash the conversation first so `/rewind clear` can restore it
2035
2081
  // (Claude-Code 2.1.191). A no-op /clear (nothing to stash) keeps any
@@ -3644,6 +3690,14 @@ export async function startAgentRepl(options = {}) {
3644
3690
  logger.verbose(`[hook] prompt rewritten by UserPromptSubmit hook`);
3645
3691
  }
3646
3692
 
3693
+ // Backstop: if the previous turn's agentLoop threw before the success-path
3694
+ // restore ran, undo its /btw aside now so it never leaks into this turn's
3695
+ // history/model call. (Normal turns clear _btwRestore right after agentLoop.)
3696
+ if (_btwRestore) {
3697
+ _btwRestore.msg.content = _btwRestore.content;
3698
+ _btwRestore = null;
3699
+ }
3700
+
3647
3701
  // Expand @path file references into context blocks (Claude-Code parity),
3648
3702
  // so `review @src/x.js` injects the file contents. Typo'd paths are warned
3649
3703
  // about and left as-is.
@@ -3746,8 +3800,10 @@ export async function startAgentRepl(options = {}) {
3746
3800
  }
3747
3801
  }
3748
3802
 
3749
- // Add user message
3750
- messages.push({ role: "user", content: _userMessageContent });
3803
+ // Add user message (keep the object ref so a /btw aside can be injected for
3804
+ // this turn's model call and then stripped before persistence).
3805
+ const _userMsg = { role: "user", content: _userMessageContent };
3806
+ messages.push(_userMsg);
3751
3807
 
3752
3808
  // Slot-filling: detect intent and fill missing parameters interactively
3753
3809
  try {
@@ -3856,6 +3912,23 @@ export async function startAgentRepl(options = {}) {
3856
3912
  }
3857
3913
  : {};
3858
3914
  if (_streamLive) process.stdout.write("\n");
3915
+ // Inject any queued /btw asides into THIS turn's user message just before
3916
+ // the model call. We remember the pre-aside content so it's restored right
3917
+ // after agentLoop returns — the aside steers this answer but is never
3918
+ // persisted (saveMessages/JSONL) or carried into later turns. Consumed on
3919
+ // send (cleared now) so a thrown turn doesn't re-inject next time; the
3920
+ // submit-start backstop restores if agentLoop throws before we get back.
3921
+ if (pendingBtw.length > 0) {
3922
+ const block = buildAsideBlock(pendingBtw);
3923
+ if (block) {
3924
+ _btwRestore = { msg: _userMsg, content: _userMsg.content };
3925
+ _userMsg.content = applyAside(_userMsg.content, block);
3926
+ logger.verbose(
3927
+ `[btw] applied ${pendingBtw.length} aside(s) to this turn`,
3928
+ );
3929
+ }
3930
+ pendingBtw = [];
3931
+ }
3859
3932
  // Consume the one-shot resume-degeneracy flag for THIS turn so the role
3860
3933
  // merge fires exactly once (2.1.187 parity; see _sanitizeRolesNextTurn).
3861
3934
  const _mergeRolesThisTurn = _sanitizeRolesNextTurn;
@@ -3913,6 +3986,7 @@ export async function startAgentRepl(options = {}) {
3913
3986
  sessionId,
3914
3987
  cwd: process.cwd(),
3915
3988
  additionalDirectories,
3989
+ sandbox: _sandbox,
3916
3990
  autoCheckpoint,
3917
3991
  checkpointSession: sessionId,
3918
3992
  checkpointMarks: _checkpointMarks,
@@ -3936,6 +4010,13 @@ export async function startAgentRepl(options = {}) {
3936
4010
  });
3937
4011
  _turnAbort = null;
3938
4012
 
4013
+ // Strip the one-shot /btw aside now the model has seen it — so it is never
4014
+ // persisted (DB saveMessages below) or carried into the next turn.
4015
+ if (_btwRestore) {
4016
+ _btwRestore.msg.content = _btwRestore.content;
4017
+ _btwRestore = null;
4018
+ }
4019
+
3939
4020
  // Running spend for `/cost` (in-memory, works without persistence).
3940
4021
  if (usageEvents?.length) addUsage(_costStore, usageEvents);
3941
4022
 
@@ -65,7 +65,13 @@ export function parseAgentsCommand(line) {
65
65
  if (first === "help") return { action: "help" };
66
66
  if (first === "show") return { action: "show", name: tokens[1] || null };
67
67
  if (first === "new") {
68
- return { action: "new", name: tokens[1] || null, description, tools, location };
68
+ return {
69
+ action: "new",
70
+ name: tokens[1] || null,
71
+ description,
72
+ tools,
73
+ location,
74
+ };
69
75
  }
70
76
  // A bare token is treated as a name to show.
71
77
  return { action: "show", name: first };
@@ -95,7 +101,7 @@ export function formatAgentsList(agents, profiles) {
95
101
  }
96
102
  lines.push(
97
103
  "",
98
- "Manage: /agents show <name> · /agents new <name> · run via `cc agents run <name> \"<task>\"`",
104
+ 'Manage: /agents show <name> · /agents new <name> · run via `cc agents run <name> "<task>"`',
99
105
  );
100
106
  return lines.join("\n");
101
107
  }
@@ -0,0 +1,56 @@
1
+ /**
2
+ * Pure helpers for the REPL `/btw` aside (Claude-Code parity, one-shot variant).
3
+ *
4
+ * `/btw <note>` queues a side note that rides ONLY the next message: it is sent
5
+ * to the model for that one turn (so it can steer the answer) and then removed,
6
+ * so it is never persisted to the session history and never carried into later
7
+ * turns — i.e. context without bloat. vscode/readline-free → unit-testable; the
8
+ * REPL owns the queue + the inject/restore around the agentLoop call.
9
+ *
10
+ * /btw I'm on Windows, use PowerShell → queued, applied to the next turn only
11
+ * /btw → usage error (nothing queued)
12
+ */
13
+
14
+ /** Parse a `/btw …` line. Returns { text } | { error } | null (not a /btw). */
15
+ export function parseBtwCommand(trimmed) {
16
+ const t = String(trimmed == null ? "" : trimmed).trim();
17
+ if (t !== "/btw" && !t.startsWith("/btw ")) return null;
18
+ const text = t.slice("/btw".length).trim();
19
+ if (!text) {
20
+ return {
21
+ error:
22
+ "usage: /btw <note> — a one-off aside for your next message (sent to the model, not saved to history)",
23
+ };
24
+ }
25
+ return { text };
26
+ }
27
+
28
+ /**
29
+ * Render queued asides into a single tagged block, or null when there is
30
+ * nothing to add. The note tells the model this is ephemeral user guidance.
31
+ */
32
+ export function buildAsideBlock(asides) {
33
+ const list = (Array.isArray(asides) ? asides : [])
34
+ .map((s) => String(s == null ? "" : s).trim())
35
+ .filter(Boolean);
36
+ if (!list.length) return null;
37
+ return (
38
+ '<aside note="one-off side note from the user — applies to THIS message only, not saved to history">\n' +
39
+ list.join("\n") +
40
+ "\n</aside>"
41
+ );
42
+ }
43
+
44
+ /**
45
+ * Append an aside block to a user message's content, preserving multimodal
46
+ * arrays (OpenAI-style content parts from --image turns). Returns the new
47
+ * content; a falsy block returns the content unchanged.
48
+ */
49
+ export function applyAside(content, block) {
50
+ if (!block) return content;
51
+ if (Array.isArray(content)) {
52
+ return [...content, { type: "text", text: block }];
53
+ }
54
+ const s = content == null ? "" : String(content);
55
+ return s.length ? `${s}\n\n${block}` : block;
56
+ }