chainlesschain 0.162.34 → 0.162.36

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 (185) hide show
  1. package/package.json +1 -1
  2. package/src/assets/web-panel/assets/{AIOps-BYfi9NYS.js → AIOps-vAVAFNJ4.js} +1 -1
  3. package/src/assets/web-panel/assets/{ActionButton-BiS_tAN7.js → ActionButton-BnRHFCKM.js} +1 -1
  4. package/src/assets/web-panel/assets/{Analytics-jiWl_p-B.js → Analytics-BOjwqWqG.js} +3 -3
  5. package/src/assets/web-panel/assets/{AppLayout-m4sIzDot.js → AppLayout-Dc0D1Txn.js} +5 -5
  6. package/src/assets/web-panel/assets/{Audit-CPla3Erm.js → Audit-dd_2efaZ.js} +1 -1
  7. package/src/assets/web-panel/assets/{Backup-BGeQzTaB.js → Backup-HF1jgm8G.js} +1 -1
  8. package/src/assets/web-panel/assets/{BaseInput-DTf7Z1iU.js → BaseInput-CCtzmoKe.js} +1 -1
  9. package/src/assets/web-panel/assets/{Chat-DPTlQlD-.js → Chat-BNfH1c3p.js} +6 -6
  10. package/src/assets/web-panel/assets/{ChatBubbleRenderer-BgRXce4e.js → ChatBubbleRenderer-DCWFqmI4.js} +1 -1
  11. package/src/assets/web-panel/assets/{Checkbox-DY-XuQMu.js → Checkbox-BOr-NscK.js} +1 -1
  12. package/src/assets/web-panel/assets/{Codegen-B6oxPiZI.js → Codegen-DE058N7-.js} +1 -1
  13. package/src/assets/web-panel/assets/{Col-Dqxb4wSE.js → Col-SOREo1XE.js} +1 -1
  14. package/src/assets/web-panel/assets/{Community-DCIX514p.js → Community-sOvNZo9f.js} +1 -1
  15. package/src/assets/web-panel/assets/{Compact-BGtCzDoJ.js → Compact-DnBe558D.js} +1 -1
  16. package/src/assets/web-panel/assets/{Compliance-zcOYd55o.js → Compliance-o-r6CUbg.js} +1 -1
  17. package/src/assets/web-panel/assets/{Cowork-DVTtdIdM.js → Cowork-D6_k9mHP.js} +4 -4
  18. package/src/assets/web-panel/assets/{Cron-CPUaR69k.js → Cron-CEV3Xkrm.js} +2 -2
  19. package/src/assets/web-panel/assets/{Crosschain-DnjUS6QH.js → Crosschain-eJ1lQWKU.js} +1 -1
  20. package/src/assets/web-panel/assets/{DID-Dnz8VDmx.js → DID-B-WqM9Hp.js} +2 -2
  21. package/src/assets/web-panel/assets/{Dashboard-CtWf27j7.js → Dashboard-ZnKPcsHN.js} +2 -2
  22. package/src/assets/web-panel/assets/{Dropdown-B4GC1ZV4.js → Dropdown-B8uLWDIP.js} +1 -1
  23. package/src/assets/web-panel/assets/{EmailListRenderer-wjij3kzr.js → EmailListRenderer-Jmj2Y7aH.js} +1 -1
  24. package/src/assets/web-panel/assets/{FamilyGuardDashboard-rS-2W4u5.js → FamilyGuardDashboard-Cb2xetG-.js} +1 -1
  25. package/src/assets/web-panel/assets/{Federation-90p5Tnoz.js → Federation-C_07GXoq.js} +1 -1
  26. package/src/assets/web-panel/assets/{FormItemContext-Cnrw7gzq.js → FormItemContext-D3kbYrMU.js} +1 -1
  27. package/src/assets/web-panel/assets/{GenericCardRenderer-C85NsWa3.js → GenericCardRenderer-9xgqvGPg.js} +1 -1
  28. package/src/assets/web-panel/assets/{Git-BFAVM9F8.js → Git-BlwWlMMB.js} +2 -2
  29. package/src/assets/web-panel/assets/{Governance-DBoRonpq.js → Governance-DxN3wQZ_.js} +1 -1
  30. package/src/assets/web-panel/assets/{Inference-DHRyD66j.js → Inference-ls7pSw_D.js} +1 -1
  31. package/src/assets/web-panel/assets/{KnowledgeGraph-CTvUKecD.js → KnowledgeGraph-_n9hYuPI.js} +1 -1
  32. package/src/assets/web-panel/assets/{Logs-CB0dv_Ts.js → Logs-CvEVY5TK.js} +2 -2
  33. package/src/assets/web-panel/assets/{Marketplace-CN7Hm5Uw.js → Marketplace-C3qvQJT7.js} +1 -1
  34. package/src/assets/web-panel/assets/{McpTools-q5H25_8L.js → McpTools-DiwKpnKx.js} +5 -5
  35. package/src/assets/web-panel/assets/{Memory-BCV3pZ1d.js → Memory-CIBPi_da.js} +2 -2
  36. package/src/assets/web-panel/assets/{MobileBridge-C04Mngt4.js → MobileBridge-D-v0Se8y.js} +2 -2
  37. package/src/assets/web-panel/assets/MobileProjects-cP1apTQD.js +1 -0
  38. package/src/assets/web-panel/assets/{Mtc-ByAMz2DN.js → Mtc-BMFWrI65.js} +4 -4
  39. package/src/assets/web-panel/assets/{MtcAudit-B7V7byJq.js → MtcAudit-2s8LaHtR.js} +2 -2
  40. package/src/assets/web-panel/assets/{Multisig-DtKmcVQV.js → Multisig-dL_nvj7d.js} +3 -3
  41. package/src/assets/web-panel/assets/{NLProgramming-CaMbT5SC.js → NLProgramming-BbrJp06R.js} +1 -1
  42. package/src/assets/web-panel/assets/{Notes-DRjbSTCU.js → Notes-jR9irwy3.js} +4 -4
  43. package/src/assets/web-panel/assets/{NotificationSettings-B9YbJID5.js → NotificationSettings-Dk-STCIX.js} +1 -1
  44. package/src/assets/web-panel/assets/{OrderTableRenderer-BcI_-vGS.js → OrderTableRenderer-CqqfY6zq.js} +1 -1
  45. package/src/assets/web-panel/assets/{Organization-oTask4BE.js → Organization-BCK5jylo.js} +4 -4
  46. package/src/assets/web-panel/assets/{Overflow-Bab06ey7.js → Overflow-BRAY7Smt.js} +1 -1
  47. package/src/assets/web-panel/assets/{P2P--wlBeU0N.js → P2P-BltVRGjb.js} +2 -2
  48. package/src/assets/web-panel/assets/{PdhVaultBrowser-D4t77Pwc.js → PdhVaultBrowser-CV8UbXHe.js} +3 -3
  49. package/src/assets/web-panel/assets/{Permissions-B3sf6CJ3.js → Permissions-_tNl47Qh.js} +4 -4
  50. package/src/assets/web-panel/assets/{PersonalDataHub-BXOojk63.js → PersonalDataHub-Cgc4HjpX.js} +4 -4
  51. package/src/assets/web-panel/assets/{Pipeline-DReqtBFN.js → Pipeline-Bn_QU4mu.js} +1 -1
  52. package/src/assets/web-panel/assets/{Privacy-cT1GwKLx.js → Privacy-jzJowp5P.js} +1 -1
  53. package/src/assets/web-panel/assets/{ProjectInit-BhTAzVhH.js → ProjectInit-B_1pJ8qd.js} +2 -2
  54. package/src/assets/web-panel/assets/{ProjectSettings-CK-D8Fyj.js → ProjectSettings-CPVZpXzs.js} +2 -2
  55. package/src/assets/web-panel/assets/{Projects-CbHiwen6.js → Projects-CQsHOWnT.js} +1 -1
  56. package/src/assets/web-panel/assets/{Providers-B-ftiXa8.js → Providers-CzzMiLC0.js} +1 -1
  57. package/src/assets/web-panel/assets/{QuickAsk-CT5XPwTF.js → QuickAsk-MxBKIn9o.js} +1 -1
  58. package/src/assets/web-panel/assets/{Recommend-CohhlBZ_.js → Recommend-D8lN6Lis.js} +1 -1
  59. package/src/assets/web-panel/assets/{Reputation-CrgbixFz.js → Reputation-CfYK-IrV.js} +1 -1
  60. package/src/assets/web-panel/assets/{Row-ClExmBn3.js → Row-Bg7NZDP9.js} +1 -1
  61. package/src/assets/web-panel/assets/{RssFeed-VV0qizCJ.js → RssFeed-BOVNJhj0.js} +3 -3
  62. package/src/assets/web-panel/assets/{Search-CqJapSiL.js → Search-B38qzmhY.js} +1 -1
  63. package/src/assets/web-panel/assets/{Security-DY66Zie6.js → Security-CjqleZpe.js} +4 -4
  64. package/src/assets/web-panel/assets/{Services-RQwxat7-.js → Services-Bu9JSJap.js} +2 -2
  65. package/src/assets/web-panel/assets/{Skeleton-0v37UTU_.js → Skeleton-B2RvRkaX.js} +1 -1
  66. package/src/assets/web-panel/assets/{Skills-B4Vm4DxN.js → Skills-_h42mxMN.js} +1 -1
  67. package/src/assets/web-panel/assets/{Sla-CggphTlo.js → Sla-BssLs56D.js} +1 -1
  68. package/src/assets/web-panel/assets/{SpeechSettings-BAOU08C7.js → SpeechSettings-DCxFYHsd.js} +1 -1
  69. package/src/assets/web-panel/assets/{SyncSettings-DmtC4J1w.js → SyncSettings-D2xQuNLE.js} +2 -2
  70. package/src/assets/web-panel/assets/Tasks-DhpOGOlo.js +1 -0
  71. package/src/assets/web-panel/assets/{Templates-C1QK0YoU.js → Templates-CYG-R-aS.js} +1 -1
  72. package/src/assets/web-panel/assets/{Tenant-CieOfmqp.js → Tenant-BQRYLsvP.js} +1 -1
  73. package/src/assets/web-panel/assets/{Terminal-DWdhrxRq.js → Terminal-imKU7N5j.js} +2 -2
  74. package/src/assets/web-panel/assets/{TimelineRenderer-CjFVUUDU.js → TimelineRenderer-BIZzBftk.js} +1 -1
  75. package/src/assets/web-panel/assets/{Tokens-Bwbk3id9.js → Tokens-uMLH5p_a.js} +1 -1
  76. package/src/assets/web-panel/assets/{Trigger-uJle_yj4.js → Trigger-BzS6XPqx.js} +1 -1
  77. package/src/assets/web-panel/assets/{Trust-BcOuxAA5.js → Trust-R4zhHufZ.js} +1 -1
  78. package/src/assets/web-panel/assets/{UkeySign-DUu7Ufg6.js → UkeySign-DATQCoGe.js} +1 -1
  79. package/src/assets/web-panel/assets/{VideoEditing-Ck8JtQ2n.js → VideoEditing-ClUmKOtS.js} +1 -1
  80. package/src/assets/web-panel/assets/{Wallet-B3jw43on.js → Wallet-DzJTbQzD.js} +4 -4
  81. package/src/assets/web-panel/assets/{WebAuthn-Baf9K0y7.js → WebAuthn-CrXrLmzQ.js} +5 -5
  82. package/src/assets/web-panel/assets/{WorkflowEditor-CTEDl_83.js → WorkflowEditor-CpvZ0Tma.js} +1 -1
  83. package/src/assets/web-panel/assets/{chat-CKV51quV.js → chat-a6wpYmVL.js} +1 -1
  84. package/src/assets/web-panel/assets/{colors-BO_RP_yz.js → colors-CXJADb1t.js} +1 -1
  85. package/src/assets/web-panel/assets/{compact-item-BZsxw_ZG.js → compact-item-CL2pohS_.js} +1 -1
  86. package/src/assets/web-panel/assets/{createContext-CAbvtzVL.js → createContext-xFi_1G5_.js} +1 -1
  87. package/src/assets/web-panel/assets/devWarning-BtmELbtB.js +1 -0
  88. package/src/assets/web-panel/assets/{hasIn-QmHT8zDz.js → hasIn-Bchh1rAi.js} +1 -1
  89. package/src/assets/web-panel/assets/{index-fnDgExTu.js → index-B3Tpv7-d.js} +1 -1
  90. package/src/assets/web-panel/assets/index-B4l4vLTB.js +1 -0
  91. package/src/assets/web-panel/assets/{index-BEJa1FiF.js → index-B4zNisy9.js} +1 -1
  92. package/src/assets/web-panel/assets/{index-jd2r-T4p.js → index-B6NehWty.js} +1 -1
  93. package/src/assets/web-panel/assets/index-B7Ek5iiY.js +1 -0
  94. package/src/assets/web-panel/assets/{index-BPZHeug4.js → index-B7knYOpm.js} +1 -1
  95. package/src/assets/web-panel/assets/{index-GPY0LjCu.js → index-B7wT5VRi.js} +1 -1
  96. package/src/assets/web-panel/assets/{index-DKnngF_f.js → index-BF4xx1_b.js} +1 -1
  97. package/src/assets/web-panel/assets/{index-BRNYA0BV.js → index-BH9t10pe.js} +1 -1
  98. package/src/assets/web-panel/assets/{index-DKquNxL2.js → index-BPH5ESqs.js} +3 -3
  99. package/src/assets/web-panel/assets/{index-CEh2Ry_A.js → index-BmsIKzyu.js} +1 -1
  100. package/src/assets/web-panel/assets/{index-Dob6B6qS.js → index-BoaRB-4a.js} +1 -1
  101. package/src/assets/web-panel/assets/{index-Ha2_56mf.js → index-BrbJBnT-.js} +1 -1
  102. package/src/assets/web-panel/assets/{index-Dln_vjSY.js → index-C2eMYASq.js} +1 -1
  103. package/src/assets/web-panel/assets/{index-CqiKnXtL.js → index-C4yBRKT4.js} +1 -1
  104. package/src/assets/web-panel/assets/{index-B3fwyCjJ.js → index-CGq4HQno.js} +1 -1
  105. package/src/assets/web-panel/assets/{index-B2aiE8jk.js → index-CMybtJY6.js} +1 -1
  106. package/src/assets/web-panel/assets/{index-B5zhcul9.js → index-CR3kFPuC.js} +1 -1
  107. package/src/assets/web-panel/assets/{index-8BMLlHCv.js → index-CTRd7vkq.js} +1 -1
  108. package/src/assets/web-panel/assets/{index-C6i3reUS.js → index-CdU8BwRW.js} +1 -1
  109. package/src/assets/web-panel/assets/{index-BNvTNZ1V.js → index-Cua_P8St.js} +1 -1
  110. package/src/assets/web-panel/assets/{index-DjrDGJP2.js → index-CuehgDOp.js} +1 -1
  111. package/src/assets/web-panel/assets/{index-BCsZiq4i.js → index-D-TT9Swq.js} +1 -1
  112. package/src/assets/web-panel/assets/{index-qPafbZmr.js → index-DEYcLAl7.js} +1 -1
  113. package/src/assets/web-panel/assets/{index-DeC7lehI.js → index-DQ_hw_5P.js} +1 -1
  114. package/src/assets/web-panel/assets/{index-BnPBG3Tr.js → index-DTEu7TSF.js} +1 -1
  115. package/src/assets/web-panel/assets/{index-D8CHQnPl.js → index-DVo1GJoj.js} +1 -1
  116. package/src/assets/web-panel/assets/{index-9IqJODII.js → index-DjdOL159.js} +1 -1
  117. package/src/assets/web-panel/assets/{index-DBCYOypV.js → index-DsbMVBj1.js} +1 -1
  118. package/src/assets/web-panel/assets/{index-BL7gQAuB.js → index-DxahxRP7.js} +1 -1
  119. package/src/assets/web-panel/assets/{index-DC1CFfQU.js → index-EPERz4Pu.js} +1 -1
  120. package/src/assets/web-panel/assets/{index-CVoYeZ5Q.js → index-IkvkNxbc.js} +1 -1
  121. package/src/assets/web-panel/assets/{index-CsBx0u5G.js → index-KCib1PTw.js} +1 -1
  122. package/src/assets/web-panel/assets/{index-5hlO2-JQ.js → index-M8SZI11a.js} +1 -1
  123. package/src/assets/web-panel/assets/{index-CSaI8R_7.js → index-TxbHusq2.js} +1 -1
  124. package/src/assets/web-panel/assets/{index-C6AA-xB2.js → index-dsLc7t6W.js} +1 -1
  125. package/src/assets/web-panel/assets/{index-DRK0oAV5.js → index-jMcv1u5o.js} +1 -1
  126. package/src/assets/web-panel/assets/{index-B9Z83FTS.js → index-majCS3s2.js} +1 -1
  127. package/src/assets/web-panel/assets/{index-C3K1eHDd.js → index-u8K1y_lh.js} +1 -1
  128. package/src/assets/web-panel/assets/{initDefaultProps-Bc2GWeWe.js → initDefaultProps-DYn3Gc09.js} +1 -1
  129. package/src/assets/web-panel/assets/{motion-BI-Rxw6o.js → motion-ZS3eolb9.js} +1 -1
  130. package/src/assets/web-panel/assets/{move-DRPdwDQB.js → move-CEw4uqr3.js} +1 -1
  131. package/src/assets/web-panel/assets/{omit-B4XTl3jW.js → omit-DlHFZnPp.js} +1 -1
  132. package/src/assets/web-panel/assets/{pickAttrs-Do5d86Wr.js → pickAttrs-eZQvV5fA.js} +1 -1
  133. package/src/assets/web-panel/assets/{placementArrow-B8VGZ0ZF.js → placementArrow-B31jQwa-.js} +1 -1
  134. package/src/assets/web-panel/assets/{responsiveObserve-Cf0kI_vN.js → responsiveObserve-DAsNmVto.js} +1 -1
  135. package/src/assets/web-panel/assets/{slide-Cb0psjSL.js → slide-gPQPrYZC.js} +1 -1
  136. package/src/assets/web-panel/assets/{statusUtils-Bjuo5Oal.js → statusUtils-DwWKX5co.js} +1 -1
  137. package/src/assets/web-panel/assets/{styleChecker-BLMhoHJ5.js → styleChecker-B3VOtXuH.js} +1 -1
  138. package/src/assets/web-panel/assets/{useFlexGapSupport-BdCwAfNU.js → useFlexGapSupport-6ADctM2r.js} +1 -1
  139. package/src/assets/web-panel/assets/{useFs-9Jhaz5gG.js → useFs-6Zx1SSKs.js} +1 -1
  140. package/src/assets/web-panel/assets/{usePersonalDataHub-xYFyXKwD.js → usePersonalDataHub-BzReowln.js} +1 -1
  141. package/src/assets/web-panel/assets/{vnode-CVhepE6Z.js → vnode-C8IpEQbD.js} +1 -1
  142. package/src/assets/web-panel/assets/{zoom-IbbtJ4Zr.js → zoom-ruc9vHr0.js} +1 -1
  143. package/src/assets/web-panel/index.html +1 -1
  144. package/src/commands/agent.js +161 -6
  145. package/src/commands/agents.js +199 -0
  146. package/src/commands/command.js +7 -2
  147. package/src/commands/hook.js +136 -28
  148. package/src/commands/ide.js +168 -0
  149. package/src/commands/mcp.js +92 -0
  150. package/src/commands/output-style.js +127 -0
  151. package/src/commands/permissions.js +211 -0
  152. package/src/commands/statusline.js +93 -0
  153. package/src/index.js +8 -0
  154. package/src/lib/agent-core.js +7 -0
  155. package/src/lib/agents.js +147 -0
  156. package/src/lib/hook-manager.js +1 -0
  157. package/src/lib/hook-runner.cjs +183 -0
  158. package/src/lib/ide-bridge.js +310 -0
  159. package/src/lib/image-input.js +156 -0
  160. package/src/lib/mcp-oauth.js +415 -0
  161. package/src/lib/output-styles.js +179 -0
  162. package/src/lib/permission-rules.cjs +325 -0
  163. package/src/lib/provider-options.js +11 -7
  164. package/src/lib/settings-hook-events.cjs +102 -0
  165. package/src/lib/settings-hooks.cjs +163 -0
  166. package/src/lib/settings-loader.cjs +244 -0
  167. package/src/lib/slash-commands.js +21 -13
  168. package/src/lib/status-line.cjs +204 -0
  169. package/src/lib/sub-agent-profiles.js +3 -0
  170. package/src/lib/web-search.js +487 -0
  171. package/src/repl/agent-repl.js +445 -35
  172. package/src/repl/slash-macro.js +45 -0
  173. package/src/runtime/agent-core.js +799 -21
  174. package/src/runtime/coding-agent-contract-shared.cjs +94 -4
  175. package/src/runtime/coding-agent-policy.cjs +24 -0
  176. package/src/runtime/headless-runner.js +162 -6
  177. package/src/runtime/headless-stream.js +133 -7
  178. package/src/runtime/mcp-config.js +161 -15
  179. package/src/runtime/policies/agent-policy.js +1 -0
  180. package/src/runtime/system-prompt.js +6 -1
  181. package/src/assets/web-panel/assets/MobileProjects-CUxONYre.js +0 -1
  182. package/src/assets/web-panel/assets/Tasks-CExqxzL6.js +0 -1
  183. package/src/assets/web-panel/assets/devWarning-DQYatsRR.js +0 -1
  184. package/src/assets/web-panel/assets/index-Bv_y1Ud7.js +0 -1
  185. package/src/assets/web-panel/assets/index-CZZnSJEX.js +0 -1
@@ -48,7 +48,10 @@ import {
48
48
  } from "../lib/task-model-selector.js";
49
49
  import { CLIPermanentMemory } from "../lib/permanent-memory.js";
50
50
  import { CLIAutonomousAgent, GoalStatus } from "../lib/autonomous-agent.js";
51
- import { PromptCompressor } from "../harness/prompt-compressor.js";
51
+ import {
52
+ PromptCompressor,
53
+ getContextWindow,
54
+ } from "../harness/prompt-compressor.js";
52
55
  import { feature } from "../lib/feature-flags.js";
53
56
  import { recordCompressionMetric } from "../lib/compression-telemetry.js";
54
57
  import {
@@ -58,17 +61,19 @@ import {
58
61
  } from "../lib/session-hooks.js";
59
62
  import { HookEvents } from "../lib/hook-manager.js";
60
63
  import { IterationBudget } from "../lib/iteration-budget.js";
61
- import { loadMcpConfig } from "../runtime/mcp-config.js";
64
+ import { resolveAgentMcp } from "../runtime/mcp-config.js";
62
65
  import {
63
66
  AGENT_TOOLS,
64
67
  buildSystemPrompt,
65
68
  executeTool as coreExecuteTool,
66
69
  agentLoop as coreAgentLoop,
67
70
  formatToolArgs,
71
+ killAllBackgroundShellTasks,
68
72
  } from "../runtime/agent-core.js";
69
73
  import { expandFileRefs } from "../runtime/file-ref-expander.js";
70
74
  import { composeSystemPrompt } from "../runtime/system-prompt.js";
71
75
  import { makeFallbackChatFn } from "../runtime/fallback-model.js";
76
+ import { resolveSlashMacro } from "./slash-macro.js";
72
77
 
73
78
  /**
74
79
  * Reference to the runtime DB for hook execution (set during startAgentRepl)
@@ -76,6 +81,61 @@ import { makeFallbackChatFn } from "../runtime/fallback-model.js";
76
81
  let _hookDb = null;
77
82
  let _compressor = null;
78
83
  let _approvalGate = null;
84
+ // .claude/settings.json permission rules (deny > ask > allow) + an interactive
85
+ // confirmer for `ask` matches. Loaded once at REPL startup; null = no file.
86
+ let _permissionRules = null;
87
+ let _permissionConfirm = null;
88
+ // .claude/settings.json `hooks` block (decision-capable PreToolUse/PostToolUse).
89
+ let _settingsHooks = null;
90
+
91
+ /**
92
+ * Fire settings.json Notification hooks (observe-only) — the agent needs the
93
+ * user's attention (e.g. waiting on a permission/risk confirmation). A hook can
94
+ * ring a bell / send a desktop notification. Best-effort, never blocks.
95
+ */
96
+ async function _fireNotification(message) {
97
+ if (!_settingsHooks) return;
98
+ try {
99
+ const { runObserveHooks } = await import("../lib/settings-hook-events.cjs");
100
+ runObserveHooks(
101
+ _settingsHooks,
102
+ "Notification",
103
+ { message, cwd: process.cwd(), session_id: null },
104
+ { cwd: process.cwd() },
105
+ );
106
+ } catch (_err) {
107
+ // observe-only — never affect the prompt
108
+ }
109
+ }
110
+
111
+ /**
112
+ * "Always allow" persistence: derive a sensible allow rule for a tool call,
113
+ * append it to .claude/settings.local.json (personal, gitignored), and reflect
114
+ * it in the in-memory ruleset so the rest of the session stops prompting. A
115
+ * persisted `allow` short-circuits the ApprovalGate via agent-core's
116
+ * `ruleAllowed` path (see permission-rules wiring). Returns {rule,file} or null.
117
+ */
118
+ async function _persistAlwaysAllow(tool, args) {
119
+ try {
120
+ const rulesMod = await import("../lib/permission-rules.cjs");
121
+ const { suggestAllowRule } = rulesMod.default || rulesMod;
122
+ const rule = suggestAllowRule(tool || "run_shell", args || {});
123
+ if (!rule) return null;
124
+ const { addRule } = await import("../lib/settings-loader.cjs");
125
+ const { file } = addRule({
126
+ cwd: process.cwd(),
127
+ kind: "allow",
128
+ rule,
129
+ scope: "local",
130
+ });
131
+ if (!_permissionRules) _permissionRules = { allow: [], ask: [], deny: [] };
132
+ if (!_permissionRules.allow.includes(rule)) _permissionRules.allow.push(rule);
133
+ return { rule, file };
134
+ } catch (err) {
135
+ process.stderr.write(` always-allow persist failed: ${err.message}\n`);
136
+ return null;
137
+ }
138
+ }
79
139
 
80
140
  /**
81
141
  * Execute a tool call — delegates to agent-core with REPL's hookDb and cwd.
@@ -85,6 +145,9 @@ async function executeTool(name, args) {
85
145
  hookDb: _hookDb,
86
146
  cwd: process.cwd(),
87
147
  approvalGate: _approvalGate,
148
+ permissionRules: _permissionRules,
149
+ permissionConfirm: _permissionConfirm,
150
+ settingsHooks: _settingsHooks,
88
151
  });
89
152
  }
90
153
 
@@ -162,6 +225,11 @@ async function agentLoop(messages, options) {
162
225
  export async function startAgentRepl(options = {}) {
163
226
  let model = options.model || "qwen2.5:7b";
164
227
  let provider = options.provider || "ollama";
228
+ // Extended thinking (Anthropic; opt-in via --think/--ultrathink). Carried from
229
+ // the runtime policy into the agent-loop options below. thinkingBudget
230
+ // (--thinking-budget) is the companion legacy-model budget_tokens override.
231
+ const thinking = options.thinking || null;
232
+ const thinkingBudget = options.thinkingBudget || null;
165
233
  const baseUrl = options.baseUrl || "http://localhost:11434";
166
234
  const apiKey = options.apiKey || null;
167
235
  // Extra workspace roots (--add-dir): advertised in the system prompt and
@@ -233,7 +301,10 @@ export async function startAgentRepl(options = {}) {
233
301
  await import("../lib/session-core-singletons.js");
234
302
  _approvalGate = await getApprovalGate();
235
303
  if (typeof _approvalGate.setConfirmer === "function") {
236
- _approvalGate.setConfirmer(async ({ args, riskLevel }) => {
304
+ _approvalGate.setConfirmer(async ({ tool, args, riskLevel }) => {
305
+ await _fireNotification(
306
+ `Permission needed: ${riskLevel || "medium"}-risk ${tool || "run_shell"}${args?.command ? " — " + args.command : ""}`,
307
+ );
237
308
  const rlConfirm = readline.createInterface({
238
309
  input: process.stdin,
239
310
  output: process.stdout,
@@ -243,13 +314,25 @@ export async function startAgentRepl(options = {}) {
243
314
  const ans = (
244
315
  await q(
245
316
  chalk.yellow(
246
- `\n[ApprovalGate] ${riskLevel || "medium"} risk command:${cmd}\n Proceed? (y/N) `,
317
+ `\n[ApprovalGate] ${riskLevel || "medium"} risk command:${cmd}\n` +
318
+ ` Proceed? [y]es once / [a]lways allow / [N]o: `,
247
319
  ),
248
320
  )
249
321
  )
250
322
  .trim()
251
323
  .toLowerCase();
252
324
  rlConfirm.close();
325
+ if (ans === "a" || ans === "always") {
326
+ const saved = await _persistAlwaysAllow(tool || "run_shell", args);
327
+ if (saved) {
328
+ process.stdout.write(
329
+ chalk.green(
330
+ ` ✓ always allow: added ${saved.rule} → ${saved.file}\n`,
331
+ ),
332
+ );
333
+ }
334
+ return true;
335
+ }
253
336
  return ans === "y" || ans === "yes";
254
337
  });
255
338
  }
@@ -257,6 +340,63 @@ export async function startAgentRepl(options = {}) {
257
340
  _approvalGate = null;
258
341
  }
259
342
 
343
+ // Load .claude/settings.json permission rules + wire an interactive confirmer
344
+ // so `ask` rules prompt (rather than fall closed like headless does).
345
+ try {
346
+ const { loadSettings } = await import("../lib/settings-loader.cjs");
347
+ const loaded = loadSettings({ cwd: process.cwd() });
348
+ const total =
349
+ loaded.rules.allow.length +
350
+ loaded.rules.ask.length +
351
+ loaded.rules.deny.length;
352
+ _permissionRules = total > 0 ? loaded.rules : null;
353
+ // Confirmer is shared by permission `ask` rules AND hook `ask` decisions,
354
+ // so define it unconditionally (a `hook:` rule label flows through too).
355
+ _permissionConfirm = async ({ tool, args, rule }) => {
356
+ await _fireNotification(
357
+ `Permission needed: ${tool}${rule ? " (" + rule + ")" : ""}`,
358
+ );
359
+ const rl = readline.createInterface({
360
+ input: process.stdin,
361
+ output: process.stdout,
362
+ });
363
+ const q = (p) => new Promise((res) => rl.question(p, res));
364
+ const detail = args?.command
365
+ ? ` ${args.command}`
366
+ : args?.path
367
+ ? ` ${args.path}`
368
+ : "";
369
+ const ans = (
370
+ await q(
371
+ chalk.yellow(
372
+ `\n[Permission] ${rule} asks before ${tool}:${detail}\n Proceed? (y/N) `,
373
+ ),
374
+ )
375
+ )
376
+ .trim()
377
+ .toLowerCase();
378
+ rl.close();
379
+ return ans === "y" || ans === "yes";
380
+ };
381
+ } catch (_err) {
382
+ _permissionRules = null;
383
+ _permissionConfirm = null;
384
+ }
385
+
386
+ // Load .claude/settings.json `hooks` block (decision-capable PreToolUse/
387
+ // PostToolUse). The interactive _permissionConfirm above doubles as the
388
+ // confirmer for a hook `ask` decision.
389
+ try {
390
+ const { loadHooks } = await import("../lib/settings-hooks.cjs");
391
+ const loaded = loadHooks({ cwd: process.cwd() });
392
+ _settingsHooks =
393
+ loaded.hooks && Object.keys(loaded.hooks).length > 0
394
+ ? loaded.hooks
395
+ : null;
396
+ } catch (_err) {
397
+ _settingsHooks = null;
398
+ }
399
+
260
400
  // Resume existing session or create new one
261
401
  const useJsonl = feature("JSONL_SESSION");
262
402
 
@@ -335,20 +475,47 @@ export async function startAgentRepl(options = {}) {
335
475
  // Non-critical — SessionManager integration must not block startup
336
476
  }
337
477
 
338
- const messages = [
478
+ // --system-prompt replaces the built-in prompt; --append-system-prompt
479
+ // extends it (parity with the headless runners). The base is kept so an
480
+ // output-style persona can be swapped in/out at runtime via /output-style.
481
+ const _replBaseSystem = composeSystemPrompt(
482
+ buildSystemPrompt(process.cwd(), { additionalDirectories }),
339
483
  {
340
- role: "system",
341
- // --system-prompt replaces the built-in prompt; --append-system-prompt
342
- // extends it (parity with the headless runners).
343
- content: composeSystemPrompt(
344
- buildSystemPrompt(process.cwd(), { additionalDirectories }),
345
- {
346
- systemPrompt: options.systemPrompt,
347
- appendSystemPrompt: options.appendSystemPrompt,
348
- },
349
- ),
484
+ systemPrompt: options.systemPrompt,
485
+ appendSystemPrompt: options.appendSystemPrompt,
350
486
  },
351
- ];
487
+ );
488
+ let _activeOutputStyle = null; // { name, body }
489
+ const messages = [{ role: "system", content: _replBaseSystem }];
490
+ // Apply --output-style or the settings.json `outputStyle` default at startup.
491
+ try {
492
+ const { resolveOutputStyle } = await import("../lib/output-styles.js");
493
+ const st = resolveOutputStyle(options.outputStyle, process.cwd());
494
+ if (st && st.body) {
495
+ _activeOutputStyle = { name: st.name, body: st.body };
496
+ messages[0].content = `${_replBaseSystem}\n\n${st.body}`;
497
+ } else if (st && st.name && !st.missing) {
498
+ _activeOutputStyle = { name: st.name, body: "" };
499
+ }
500
+ } catch (_err) {
501
+ // best-effort — no output style
502
+ }
503
+
504
+ // settings.json SessionStart hooks → inject session context (observe-only).
505
+ if (_settingsHooks) {
506
+ try {
507
+ const { runSessionStartHooks } = await import(
508
+ "../lib/settings-hook-events.cjs"
509
+ );
510
+ const ctx = runSessionStartHooks(_settingsHooks, {
511
+ source: "startup",
512
+ cwd: process.cwd(),
513
+ }).additionalContext;
514
+ if (ctx) messages.push({ role: "system", content: ctx });
515
+ } catch (_err) {
516
+ // best-effort
517
+ }
518
+ }
352
519
 
353
520
  // Deep Agents Deploy Phase 1 — load agent bundle if --bundle provided.
354
521
  // Injects AGENTS.md as system prompt, seeds USER.md into MemoryStore,
@@ -451,22 +618,30 @@ export async function startAgentRepl(options = {}) {
451
618
  }
452
619
  }
453
620
 
454
- // --mcp-config: connect ad-hoc MCP servers for this interactive session and
455
- // expose their tools to the LLM (Claude-Code parity with headless). Reuses the
456
- // shared engine, so tools surface as mcp__<server>__<tool>. Best-effort: a bad
457
- // config is reported but never aborts the REPL.
458
- if (options.mcpConfig) {
621
+ // MCP for this interactive session: the ad-hoc --mcp-config file PLUS the
622
+ // servers registered with `cc mcp add --auto-connect`, combined into one
623
+ // client so their tools surface to the LLM as mcp__<server>__<tool>. Reuses
624
+ // the shared engine. Best-effort: a bad --mcp-config is reported but never
625
+ // aborts the REPL; --no-mcp skips the registered set.
626
+ {
459
627
  try {
460
- _adhocMcp = await loadMcpConfig(options.mcpConfig, {
461
- writeErr: (s) => process.stderr.write(s),
462
- });
463
- const toolCount = _adhocMcp.extraToolDefinitions.length;
464
- logger.log(
465
- chalk.gray(
466
- `MCP: ${_adhocMcp.connected.length} server(s), ${toolCount} tool(s) ` +
467
- `(mcp__<server>__<tool>)`,
468
- ),
628
+ _adhocMcp = await resolveAgentMcp(
629
+ {
630
+ mcpConfigPath: options.mcpConfig || null,
631
+ db: db?.getDatabase?.() || null,
632
+ includeRegistered: options.useRegisteredMcp !== false,
633
+ },
634
+ { writeErr: (s) => process.stderr.write(s) },
469
635
  );
636
+ if (_adhocMcp) {
637
+ const toolCount = _adhocMcp.extraToolDefinitions.length;
638
+ logger.log(
639
+ chalk.gray(
640
+ `MCP: ${_adhocMcp.connected.length} server(s), ${toolCount} tool(s) ` +
641
+ `(mcp__<server>__<tool>)`,
642
+ ),
643
+ );
644
+ }
470
645
  } catch (mcpErr) {
471
646
  logger.log(chalk.yellow(`MCP: --mcp-config failed — ${mcpErr.message}`));
472
647
  _adhocMcp = null;
@@ -575,7 +750,58 @@ export async function startAgentRepl(options = {}) {
575
750
  );
576
751
  logger.log(chalk.gray("Type /exit to quit, /help for commands\n"));
577
752
 
753
+ // statusLine (Claude-Code parity): a line above the prompt each turn.
754
+ // - A user-configured `.claude/settings.json` `statusLine` command wins
755
+ // (model / branch / cost / … — first stdout line; best-effort, sync).
756
+ // - Otherwise a BUILT-IN context-usage line is shown: model · ⛁ used/window
757
+ // (pct%) · cwd · turn N — the "上下文用量显示" half. Default-on.
758
+ // - Suppressed entirely by `statusLine: false`, env CC_STATUSLINE=0, or
759
+ // `/statusline off`. Token usage is fed in from each turn's usage events.
760
+ let _statusLineEnabled = process.env.CC_STATUSLINE !== "0";
761
+ let _customStatus = false; // true when a settings.json command is configured
762
+ let _curModel = model; // tracks the per-turn active model for the readout
763
+ let _ctxUsedTokens = 0;
764
+ let _turnCount = 0;
765
+ let _renderStatus = null;
766
+ try {
767
+ const slm = await import("../lib/status-line.cjs");
768
+ const _sl = slm.default || slm;
769
+ const _slCfg = _sl.loadStatusLineConfig({ cwd: process.cwd() });
770
+ _customStatus = !!_slCfg;
771
+ const _slDisabled = _sl.isStatusLineDisabled({ cwd: process.cwd() });
772
+ if (_slDisabled) _statusLineEnabled = false;
773
+ _renderStatus = () => {
774
+ if (!_statusLineEnabled) return null;
775
+ try {
776
+ const context = _sl.buildContext({
777
+ sessionId,
778
+ model: _curModel,
779
+ provider,
780
+ cwd: process.cwd(),
781
+ usedTokens: _ctxUsedTokens,
782
+ contextWindow: getContextWindow(_curModel, provider),
783
+ turn: _turnCount,
784
+ });
785
+ // Custom command wins; otherwise the built-in context-usage render.
786
+ if (_slCfg) {
787
+ return _sl.renderStatusLine(_slCfg, context, { cwd: process.cwd() });
788
+ }
789
+ const line = _sl.renderDefaultStatusLine(context);
790
+ return line && line.trim() ? line : null;
791
+ } catch {
792
+ return null; // never let the status line break the REPL
793
+ }
794
+ };
795
+ } catch {
796
+ _renderStatus = null;
797
+ }
798
+
578
799
  const prompt = () => {
800
+ if (_statusLineEnabled && _renderStatus) {
801
+ const line = _renderStatus();
802
+ // Built-in line is dimmed; a custom command may carry its own ANSI.
803
+ if (line) process.stdout.write((_customStatus ? line : chalk.dim(line)) + "\n");
804
+ }
579
805
  rl.setPrompt(getPrompt());
580
806
  rl.prompt();
581
807
  };
@@ -612,6 +838,9 @@ export async function startAgentRepl(options = {}) {
612
838
  );
613
839
  logger.log(` ${chalk.cyan("/provider")} Show/change provider`);
614
840
  logger.log(` ${chalk.cyan("/clear")} Clear conversation`);
841
+ logger.log(
842
+ ` ${chalk.cyan("/statusline")} Context-usage line on/off (/statusline [on|off])`,
843
+ );
615
844
  logger.log(
616
845
  ` ${chalk.cyan("/compact")} Smart compact (importance-based)`,
617
846
  );
@@ -658,6 +887,29 @@ export async function startAgentRepl(options = {}) {
658
887
  logger.log(
659
888
  " Context engineering: instinct + memory + notes injection\n",
660
889
  );
890
+ // User-defined command macros (.claude/commands/*.md) become runnable
891
+ // slash commands here — list whatever is discovered so they're visible.
892
+ try {
893
+ const { discoverCommands } = await import("../lib/slash-commands.js");
894
+ const macros = discoverCommands(process.cwd());
895
+ if (macros.length > 0) {
896
+ logger.log(chalk.bold("Custom commands (.claude/commands):"));
897
+ for (const m of macros) {
898
+ const tag =
899
+ m.scope === "project"
900
+ ? chalk.cyan("[proj]")
901
+ : chalk.gray("[pers]");
902
+ logger.log(
903
+ ` ${chalk.cyan("/" + m.name)} ${tag}` +
904
+ (m.argumentHint ? chalk.dim(` ${m.argumentHint}`) : "") +
905
+ (m.description ? ` ${chalk.gray(m.description)}` : ""),
906
+ );
907
+ }
908
+ logger.log("");
909
+ }
910
+ } catch (_err) {
911
+ // Non-critical — macro discovery failure must not break /help
912
+ }
661
913
  prompt();
662
914
  return;
663
915
  }
@@ -708,6 +960,7 @@ export async function startAgentRepl(options = {}) {
708
960
  const arg = trimmed.slice(6).trim();
709
961
  if (arg) {
710
962
  model = arg;
963
+ _curModel = model; // keep the status-line readout in sync
711
964
  logger.info(`Model: ${chalk.cyan(model)}`);
712
965
  } else {
713
966
  logger.info(`Current model: ${chalk.cyan(model)}`);
@@ -756,6 +1009,76 @@ export async function startAgentRepl(options = {}) {
756
1009
  return;
757
1010
  }
758
1011
 
1012
+ if (trimmed === "/statusline" || trimmed.startsWith("/statusline ")) {
1013
+ const arg = trimmed.slice("/statusline".length).trim().toLowerCase();
1014
+ if (arg === "off") {
1015
+ _statusLineEnabled = false;
1016
+ logger.info("Status line: off");
1017
+ } else if (arg === "on") {
1018
+ _statusLineEnabled = true;
1019
+ logger.info("Status line: on");
1020
+ } else {
1021
+ // bare / "show" → report state + a one-off render
1022
+ const line = _statusLineEnabled && _renderStatus ? _renderStatus() : null;
1023
+ if (line) {
1024
+ logger.info(
1025
+ `Status line: ${_customStatus ? line : chalk.dim(line)}`,
1026
+ );
1027
+ } else {
1028
+ logger.info(
1029
+ `Status line: ${_statusLineEnabled ? "on (no content yet)" : "off"}` +
1030
+ (_statusLineEnabled ? "" : ` — enable with ${chalk.cyan("/statusline on")}`),
1031
+ );
1032
+ }
1033
+ if (_customStatus) {
1034
+ logger.info(chalk.gray(" source: settings.json statusLine command"));
1035
+ }
1036
+ }
1037
+ prompt();
1038
+ return;
1039
+ }
1040
+
1041
+ if (trimmed === "/output-style" || trimmed.startsWith("/output-style ")) {
1042
+ const arg = trimmed.slice("/output-style".length).trim();
1043
+ try {
1044
+ const { discoverOutputStyles, getOutputStyle } = await import(
1045
+ "../lib/output-styles.js"
1046
+ );
1047
+ if (!arg) {
1048
+ logger.log(chalk.bold("Output styles:"));
1049
+ for (const s of discoverOutputStyles(process.cwd())) {
1050
+ const cur =
1051
+ _activeOutputStyle?.name === s.name ? chalk.green(" *") : "";
1052
+ logger.log(
1053
+ ` ${s.name.padEnd(16)}${cur} ${chalk.gray(s.description || "")}`,
1054
+ );
1055
+ }
1056
+ logger.log(
1057
+ chalk.gray(`current: ${_activeOutputStyle?.name || "none"}`),
1058
+ );
1059
+ } else if (arg === "none" || arg === "default") {
1060
+ _activeOutputStyle = null;
1061
+ messages[0].content = _replBaseSystem;
1062
+ logger.info("output style cleared");
1063
+ } else {
1064
+ const s = getOutputStyle(arg, process.cwd());
1065
+ if (!s) {
1066
+ logger.error(chalk.red(`no such output style: ${arg}`));
1067
+ } else {
1068
+ _activeOutputStyle = { name: s.name, body: s.body || "" };
1069
+ messages[0].content = s.body
1070
+ ? `${_replBaseSystem}\n\n${s.body}`
1071
+ : _replBaseSystem;
1072
+ logger.info(chalk.green(`output style → ${s.name}`));
1073
+ }
1074
+ }
1075
+ } catch (err) {
1076
+ logger.error(chalk.red(`/output-style failed: ${err.message}`));
1077
+ }
1078
+ prompt();
1079
+ return;
1080
+ }
1081
+
759
1082
  if (trimmed === "/compact") {
760
1083
  if (_compressor && messages.length > 3) {
761
1084
  const { messages: compacted, stats } = await _compressor.compress(
@@ -1473,10 +1796,28 @@ export async function startAgentRepl(options = {}) {
1473
1796
  return;
1474
1797
  }
1475
1798
 
1799
+ // User-defined slash-command macros (.claude/commands/*.md), Claude-Code
1800
+ // parity. resolveSlashMacro maps a leading /name to a command macro and
1801
+ // expands its template; a non-match returns the line unchanged so a literal
1802
+ // prompt like "/etc/hosts" still reaches the LLM. Wire is unit-tested.
1803
+ let promptText = trimmed;
1804
+ try {
1805
+ const macro = await resolveSlashMacro(trimmed, { cwd: process.cwd() });
1806
+ if (macro.matched) {
1807
+ for (const w of macro.warnings) logger.info(chalk.yellow(`[@ref] ${w}`));
1808
+ promptText = macro.promptText;
1809
+ logger.log(
1810
+ chalk.gray(`[/${macro.name}] macro expanded (${macro.scope})`),
1811
+ );
1812
+ }
1813
+ } catch (err) {
1814
+ logger.verbose(`[slash-macro] expansion skipped: ${err.message}`);
1815
+ }
1816
+
1476
1817
  // Fire UserPromptSubmit hook with rewrite/abort support.
1477
1818
  // Hooks may emit {"rewrittenPrompt": "..."} or {"abort": true, "reason": "..."}
1478
1819
  // via stdout JSON. Failures fall through to the original prompt.
1479
- const promptDirective = await fireUserPromptSubmit(_hookDb, trimmed, {
1820
+ const promptDirective = await fireUserPromptSubmit(_hookDb, promptText, {
1480
1821
  sessionId,
1481
1822
  messageCount: messages.length,
1482
1823
  });
@@ -1490,7 +1831,7 @@ export async function startAgentRepl(options = {}) {
1490
1831
  return;
1491
1832
  }
1492
1833
  const effectivePrompt = promptDirective.prompt;
1493
- if (effectivePrompt !== trimmed) {
1834
+ if (effectivePrompt !== promptText) {
1494
1835
  logger.verbose(`[hook] prompt rewritten by UserPromptSubmit hook`);
1495
1836
  }
1496
1837
 
@@ -1514,13 +1855,42 @@ export async function startAgentRepl(options = {}) {
1514
1855
  logger.verbose(`[@ref] expansion skipped: ${err.message}`);
1515
1856
  }
1516
1857
 
1858
+ // settings.json UserPromptSubmit hooks (decision-capable; the DB hook above
1859
+ // is observe-only). block → abort the turn; context → inject before the turn.
1860
+ if (_settingsHooks) {
1861
+ try {
1862
+ const { runUserPromptSubmitHooks } = await import(
1863
+ "../lib/settings-hook-events.cjs"
1864
+ );
1865
+ const ups = runUserPromptSubmitHooks(_settingsHooks, {
1866
+ prompt: userContent,
1867
+ cwd: process.cwd(),
1868
+ sessionId,
1869
+ });
1870
+ if (ups.blocked) {
1871
+ logger.info(
1872
+ chalk.yellow(
1873
+ `[hook] prompt blocked${ups.reason ? ": " + ups.reason : ""}`,
1874
+ ),
1875
+ );
1876
+ prompt();
1877
+ return;
1878
+ }
1879
+ if (ups.additionalContext) {
1880
+ userContent += `\n\n[hook context]\n${ups.additionalContext}`;
1881
+ }
1882
+ } catch (_err) {
1883
+ // settings hook dispatch is best-effort
1884
+ }
1885
+ }
1886
+
1517
1887
  // Add user message
1518
1888
  messages.push({ role: "user", content: userContent });
1519
1889
 
1520
1890
  // Slot-filling: detect intent and fill missing parameters interactively
1521
1891
  try {
1522
1892
  const { CLISlotFiller } = await import("../lib/slot-filler.js");
1523
- const intent = CLISlotFiller.detectIntent(trimmed);
1893
+ const intent = CLISlotFiller.detectIntent(promptText);
1524
1894
  if (intent) {
1525
1895
  const defs = CLISlotFiller.getSlotDefinitions(intent.type);
1526
1896
  const missing = defs.required.filter((s) => !intent.entities[s]);
@@ -1549,7 +1919,7 @@ export async function startAgentRepl(options = {}) {
1549
1919
 
1550
1920
  // Auto-select best model based on task type
1551
1921
  let activeModel = model;
1552
- const taskDetection = detectTaskType(trimmed);
1922
+ const taskDetection = detectTaskType(promptText);
1553
1923
  if (taskDetection.confidence > 0.3) {
1554
1924
  const recommended = selectModelForTask(provider, taskDetection.taskType);
1555
1925
  if (recommended && recommended !== activeModel) {
@@ -1583,6 +1953,8 @@ export async function startAgentRepl(options = {}) {
1583
1953
  const { content: response, usageEvents } = await agentLoop(messages, {
1584
1954
  provider,
1585
1955
  model: activeModel,
1956
+ thinking,
1957
+ thinkingBudget,
1586
1958
  baseUrl,
1587
1959
  apiKey,
1588
1960
  contextEngine,
@@ -1594,6 +1966,9 @@ export async function startAgentRepl(options = {}) {
1594
1966
  checkpointSession: sessionId,
1595
1967
  prepareCall,
1596
1968
  approvalGate: _approvalGate,
1969
+ permissionRules: _permissionRules,
1970
+ permissionConfirm: _permissionConfirm,
1971
+ settingsHooks: _settingsHooks,
1597
1972
  // MCP: --mcp-config (ad-hoc) wins; bundle MCP is the fallback. The 3
1598
1973
  // tool channels expose --mcp-config servers' tools to the LLM directly.
1599
1974
  mcpClient: _adhocMcp?.mcpClient || _bundleMcpClient || undefined,
@@ -1617,6 +1992,17 @@ export async function startAgentRepl(options = {}) {
1617
1992
  }
1618
1993
  }
1619
1994
 
1995
+ // Feed the status line: the last usage event's input+output ≈ the tokens
1996
+ // now resident in the context window (what the next call resends). Track
1997
+ // the active model too, so the built-in readout reflects auto-switches.
1998
+ _curModel = activeModel;
1999
+ _turnCount += 1;
2000
+ if (usageEvents?.length) {
2001
+ const last = usageEvents[usageEvents.length - 1]?.usage || {};
2002
+ const used = (last.input_tokens || 0) + (last.output_tokens || 0);
2003
+ if (used > 0) _ctxUsedTokens = used;
2004
+ }
2005
+
1620
2006
  // Fire AssistantResponse hook with rewrite/suppress support
1621
2007
  const responseDirective = await fireAssistantResponse(
1622
2008
  _hookDb,
@@ -1710,7 +2096,7 @@ export async function startAgentRepl(options = {}) {
1710
2096
  // Store as episodic memory
1711
2097
  if (db) {
1712
2098
  try {
1713
- storeMemory(db, trimmed, { importance: 0.3, type: "episodic" });
2099
+ storeMemory(db, promptText, { importance: 0.3, type: "episodic" });
1714
2100
  } catch (_e) {
1715
2101
  // Non-critical
1716
2102
  }
@@ -1742,6 +2128,22 @@ export async function startAgentRepl(options = {}) {
1742
2128
  });
1743
2129
 
1744
2130
  rl.on("close", async () => {
2131
+ // settings.json SessionEnd hooks (observe-only) when the REPL exits.
2132
+ if (_settingsHooks) {
2133
+ try {
2134
+ const { runObserveHooks } = await import(
2135
+ "../lib/settings-hook-events.cjs"
2136
+ );
2137
+ runObserveHooks(
2138
+ _settingsHooks,
2139
+ "SessionEnd",
2140
+ { reason: "exit", cwd: process.cwd(), session_id: sessionId },
2141
+ { cwd: process.cwd() },
2142
+ );
2143
+ } catch (_err) {
2144
+ // observe-only
2145
+ }
2146
+ }
1745
2147
  // Save session on exit
1746
2148
  if (sessionId) {
1747
2149
  try {
@@ -1819,6 +2221,14 @@ export async function startAgentRepl(options = {}) {
1819
2221
  }
1820
2222
  }
1821
2223
 
2224
+ // Kill any background run_shell tasks so a backgrounded command (e.g. a
2225
+ // dev server) doesn't outlive the REPL session.
2226
+ try {
2227
+ killAllBackgroundShellTasks();
2228
+ } catch (_e) {
2229
+ // Non-critical
2230
+ }
2231
+
1822
2232
  // Shutdown runtime
1823
2233
  try {
1824
2234
  await shutdown();