@qwen-code/qwen-code 0.19.2 → 0.19.3-nightly.20260630.e00fe6a27

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 (286) hide show
  1. package/bundled/extension-creator/SKILL.md +357 -0
  2. package/bundled/loop/SKILL.md +21 -7
  3. package/bundled/loop/SKILL.test.ts +41 -0
  4. package/bundled/loop/loop-task-file.test.ts +1064 -0
  5. package/bundled/loop/loop-task-file.ts +404 -0
  6. package/bundled/loop/loop-tick-resolver.test.ts +665 -0
  7. package/bundled/loop/loop-tick-resolver.ts +347 -0
  8. package/bundled/qc-helper/SKILL.md +26 -19
  9. package/bundled/qc-helper/docs/configuration/auth.md +11 -11
  10. package/bundled/qc-helper/docs/configuration/model-providers.md +13 -13
  11. package/bundled/qc-helper/docs/configuration/settings.md +76 -75
  12. package/bundled/qc-helper/docs/features/channels/plugins.md +1 -1
  13. package/bundled/qc-helper/docs/features/commands.md +108 -77
  14. package/bundled/qc-helper/docs/features/hooks.md +6 -1
  15. package/bundled/qc-helper/docs/features/memory.md +52 -0
  16. package/bundled/qc-helper/docs/qwen-serve.md +24 -2
  17. package/bundled/qc-helper/docs/support/tos-privacy.md +16 -3
  18. package/bundled/qc-helper/docs/support/troubleshooting.md +6 -0
  19. package/chunks/{MaxSizedBox-7RERNLFK.js → MaxSizedBox-T5X5MN5Q.js} +29 -27
  20. package/chunks/{StandaloneSessionPicker-S22XBSN6.js → StandaloneSessionPicker-P3E3WTOQ.js} +42 -37
  21. package/chunks/{acpAgent-4MCETHF2.js → acpAgent-QEP6DXFW.js} +864 -175
  22. package/chunks/{agent-K3ENVYSB.js → agent-UDANDUF7.js} +24 -23
  23. package/chunks/{agent-headless-HO3V3A3A.js → agent-headless-KYVSZHSO.js} +24 -23
  24. package/chunks/{anthropicContentGenerator-E3TQ5XJJ.js → anthropicContentGenerator-KWSJAQ7B.js} +33 -12
  25. package/chunks/{artifact-tool-QA4KRO3B.js → artifact-tool-D2X7G3C3.js} +2 -2
  26. package/chunks/{askUserQuestion-HMWXW6VN.js → askUserQuestion-UGETICJQ.js} +2 -2
  27. package/chunks/{bridge-4LV2HBH2.js → bridge-X42LXPTA.js} +31 -30
  28. package/chunks/{ca-7WC7ELX6.js → ca-IUFWESZL.js} +1 -0
  29. package/chunks/{chunk-JXQ23SNT.js → chunk-2JKEQWDZ.js} +623 -78
  30. package/chunks/{chunk-2TJ3YN6Q.js → chunk-2RJA6J57.js} +416 -401
  31. package/chunks/{chunk-3UALQ56H.js → chunk-3227DZO4.js} +1 -1
  32. package/chunks/{chunk-BGUDAULQ.js → chunk-3CPCDXFR.js} +5 -5
  33. package/chunks/{chunk-5RVSM6NW.js → chunk-3HUHAQF3.js} +1 -1
  34. package/chunks/{chunk-D5E5TMYH.js → chunk-3M4W4U5G.js} +16 -2
  35. package/chunks/{chunk-DBZUXSSV.js → chunk-4X7NIEQJ.js} +3 -3
  36. package/chunks/chunk-5GOVVXEV.js +73 -0
  37. package/chunks/{chunk-2BAQ3ACS.js → chunk-5SM4VT6V.js} +405 -5
  38. package/chunks/{chunk-NYOHZYHM.js → chunk-5XQ4A26L.js} +45 -3
  39. package/chunks/{chunk-CI5O6SQL.js → chunk-6FKEVYCD.js} +4 -4
  40. package/chunks/{chunk-ZD3NW5KL.js → chunk-7LM4XHPE.js} +63 -9
  41. package/chunks/chunk-7UNP2XM7.js +840 -0
  42. package/chunks/{chunk-OQHQHJRK.js → chunk-ARYCHZ4B.js} +151 -4775
  43. package/chunks/chunk-BC3C4F3Q.js +40 -0
  44. package/chunks/{chunk-CGEGHF2C.js → chunk-BKXBARJZ.js} +1 -1
  45. package/chunks/chunk-BMFMJINR.js +45 -0
  46. package/chunks/chunk-BOJWHRMX.js +11273 -0
  47. package/chunks/{chunk-CGOMICBE.js → chunk-BSZEQNUA.js} +34 -1
  48. package/chunks/{chunk-OIC6S73E.js → chunk-BUUQ5HPX.js} +1 -1
  49. package/chunks/{chunk-2BIF2NVV.js → chunk-BVSVIN45.js} +2 -2
  50. package/chunks/{chunk-TOEGRAVX.js → chunk-BX5YO7UW.js} +17 -5
  51. package/chunks/{chunk-4W4T77MO.js → chunk-BXVWV4GI.js} +109 -6
  52. package/chunks/{chunk-VQRYPFCV.js → chunk-CYHVPQYT.js} +10 -460
  53. package/chunks/{chunk-PRYMDRBV.js → chunk-DDMOU62Q.js} +1 -1
  54. package/chunks/{chunk-S566HKUS.js → chunk-DHXWNSJA.js} +15 -1
  55. package/chunks/{chunk-F6XNTYNE.js → chunk-E6XQPQXW.js} +30 -307
  56. package/chunks/{chunk-QBZ22PQX.js → chunk-EWALXUAD.js} +3 -1
  57. package/chunks/{chunk-EL5IHQON.js → chunk-EWYQFWYF.js} +6 -27
  58. package/chunks/{chunk-HG6YKEBI.js → chunk-F2DOT6UZ.js} +161 -30
  59. package/chunks/{chunk-JTYRZODB.js → chunk-FOOIZCWL.js} +16 -7
  60. package/chunks/{chunk-EX4W6ZYG.js → chunk-G5G4TFZ7.js} +1 -1
  61. package/chunks/{chunk-EW6C27XX.js → chunk-GEHVIWAT.js} +9 -3
  62. package/chunks/chunk-HEVBZSXG.js +4923 -0
  63. package/chunks/chunk-HFQ2XSE7.js +690 -0
  64. package/chunks/{chunk-PF523XJX.js → chunk-HPGTGMP3.js} +1 -1
  65. package/chunks/{chunk-OTBK43JR.js → chunk-HYCC3XM3.js} +1 -1
  66. package/chunks/{chunk-TMBPDHXD.js → chunk-I4H7PRWQ.js} +1 -1
  67. package/chunks/{chunk-EEEZIVFJ.js → chunk-JOLIREYR.js} +1 -1
  68. package/chunks/{chunk-TZM5JIAX.js → chunk-JYPQTRA3.js} +10 -7
  69. package/chunks/chunk-K5XU6E4T.js +167 -0
  70. package/chunks/{chunk-BS64PXY3.js → chunk-KFVU7ODN.js} +1 -1
  71. package/chunks/{chunk-3RJ3ZPPR.js → chunk-KKQWC6WS.js} +7 -7
  72. package/chunks/{chunk-3QFE4OTE.js → chunk-LBW7NW7X.js} +4 -4
  73. package/chunks/{chunk-Z7XXFPGP.js → chunk-LDWTYE33.js} +1 -1
  74. package/chunks/{chunk-EXMX5YHG.js → chunk-LP5E4YIH.js} +1 -1
  75. package/chunks/{chunk-ZUAXI2OA.js → chunk-M2U5NM24.js} +188 -11366
  76. package/chunks/{chunk-I5T6PIBQ.js → chunk-MBV2CTRL.js} +1010 -925
  77. package/chunks/{chunk-5JZLXFPD.js → chunk-MTHMXSNT.js} +11 -11
  78. package/chunks/{chunk-FPACXT7T.js → chunk-MTJVHQDP.js} +534 -164
  79. package/chunks/chunk-NDOC6M5S.js +481 -0
  80. package/chunks/{chunk-HGNSRCRB.js → chunk-NL4C3H6C.js} +1 -1
  81. package/chunks/{chunk-GZCJS5WH.js → chunk-O2VBQ3Y2.js} +2 -2
  82. package/chunks/{chunk-UN3C6GOR.js → chunk-PPFWEV7G.js} +10 -108
  83. package/chunks/{chunk-RS4HRC75.js → chunk-QI2CFVRF.js} +85 -8
  84. package/chunks/{chunk-XU5P23GN.js → chunk-QLDPRILK.js} +3 -3
  85. package/chunks/chunk-QYUE6W3T.js +123 -0
  86. package/chunks/{chunk-USGMPVOY.js → chunk-R3LHDWF2.js} +21 -12
  87. package/chunks/{chunk-BNXANYLH.js → chunk-RD6YJJJ6.js} +81 -1
  88. package/chunks/{chunk-PXFFKNCO.js → chunk-RIP623NO.js} +4877 -2469
  89. package/chunks/{chunk-XKBP7M7A.js → chunk-RYBSUGOR.js} +142 -30
  90. package/chunks/{chunk-BG4BIP7F.js → chunk-SBBN4BWR.js} +4 -2
  91. package/chunks/{chunk-T2VDEFUU.js → chunk-SKQZZVAV.js} +2 -2
  92. package/chunks/{chunk-5R4SBCX6.js → chunk-SL5MOGQH.js} +3 -3
  93. package/chunks/chunk-SMXULRAN.js +37 -0
  94. package/chunks/{chunk-WA5OJGFL.js → chunk-SS6WJMQK.js} +363 -76
  95. package/chunks/{chunk-UAARVVBI.js → chunk-TNWFHGN6.js} +1 -1
  96. package/chunks/{chunk-I2RFAW6Q.js → chunk-TT6Q3EN4.js} +7 -5
  97. package/chunks/chunk-UAKXW3DN.js +30 -0
  98. package/chunks/{chunk-C4K3FEQ2.js → chunk-UX4SGCGN.js} +32 -3
  99. package/chunks/{chunk-J6AW7O5D.js → chunk-WHDQZHRU.js} +2 -2
  100. package/chunks/{chunk-WGAEWQTF.js → chunk-WN6K47NR.js} +1 -2
  101. package/chunks/{chunk-UAARJR2G.js → chunk-WQT5SOI6.js} +2 -2
  102. package/chunks/{chunk-VYFMN7C6.js → chunk-WUMVIRTN.js} +225 -37
  103. package/chunks/{chunk-R36VMVEP.js → chunk-XJMKOTVV.js} +5 -5
  104. package/chunks/{chunk-TXVGVZYR.js → chunk-XJYUPHRC.js} +3 -3
  105. package/chunks/{chunk-2JD55C6G.js → chunk-Y6YMRS3U.js} +2 -2
  106. package/chunks/{chunk-LLFFKXZZ.js → chunk-YALKC62D.js} +56 -29
  107. package/chunks/{chunk-GYZQA2FO.js → chunk-YLZJCUH6.js} +1 -1
  108. package/chunks/chunk-YTFBURQD.js +313 -0
  109. package/chunks/{chunk-KBXLIVWQ.js → chunk-ZFPRYHWR.js} +2 -2
  110. package/chunks/{chunk-X6EDWN44.js → chunk-ZP6UKOPB.js} +1 -1
  111. package/chunks/{computer-use-6BBRNT6S.js → computer-use-LF2S6LUZ.js} +103 -49
  112. package/chunks/{contextCommand-UJAJ6N57.js → contextCommand-VPX5UG55.js} +26 -25
  113. package/chunks/{cron-create-4KQE6YLH.js → cron-create-SKEY55RG.js} +4 -4
  114. package/chunks/{cron-delete-THME66UP.js → cron-delete-I3NAT32Z.js} +6 -6
  115. package/chunks/{cron-list-7A54SJ5N.js → cron-list-6XRRL6TH.js} +5 -5
  116. package/chunks/{daemon-status-provider-CSL3BUOY.js → daemon-status-provider-5JAWZS4V.js} +34 -34
  117. package/chunks/{de-ACHALDED.js → de-EOW5PVHF.js} +1 -0
  118. package/chunks/{dist-BD27IA3L.js → dist-4FCNM6KJ.js} +2 -2
  119. package/chunks/{dist-OD2FY543.js → dist-O3H4PABL.js} +2 -2
  120. package/chunks/{dist-BOOH5JWN.js → dist-SCAAUWPS.js} +18 -4
  121. package/chunks/{dist-QHJRBCMP.js → dist-UKPWQJCL.js} +44 -2
  122. package/chunks/{dist-QFFZWACU.js → dist-VD7HXTML.js} +20 -9
  123. package/chunks/{earlyInputCapture-X6KMCSHD.js → earlyInputCapture-7FRQMU4P.js} +25 -24
  124. package/chunks/{edit-7J6WXARF.js → edit-REKJDIRL.js} +68 -27
  125. package/chunks/{en-NCUBOJOY.js → en-HPWEJBTR.js} +8 -0
  126. package/chunks/{enter-worktree-KULCKJPJ.js → enter-worktree-GBKUDCD3.js} +24 -23
  127. package/chunks/{enterPlanMode-H2CIOLMH.js → enterPlanMode-WB3Y4FGT.js} +24 -23
  128. package/chunks/{errors-6G2JYR54.js → errors-2XJDYO3M.js} +26 -25
  129. package/chunks/{exit-worktree-ECBWBVC5.js → exit-worktree-KKC7O2ZV.js} +24 -23
  130. package/chunks/{exitPlanMode-DQV56GTW.js → exitPlanMode-7HMS3TF7.js} +24 -23
  131. package/chunks/{fast-path-35JJFS3C.js → fast-path-XVNIYWGG.js} +4 -4
  132. package/chunks/{fast-path-settings-TSVI53EB.js → fast-path-settings-UQLRFDQP.js} +3 -2
  133. package/chunks/{fr-GVKZ27MF.js → fr-BN5NKSAC.js} +1 -0
  134. package/chunks/{gemini-NDTG7WAX.js → gemini-U5TPU3IQ.js} +210 -104
  135. package/chunks/{geminiContentGenerator-XTG6SFLA.js → geminiContentGenerator-ZVWRHXOO.js} +4 -4
  136. package/chunks/{glob-SJ2JBZK4.js → glob-FEJWIH2W.js} +24 -23
  137. package/chunks/{grep-BRMWFEWR.js → grep-KMP25IIA.js} +24 -23
  138. package/chunks/{headlessSafetyWarnings-LJ6FJX77.js → headlessSafetyWarnings-5DEDET4M.js} +24 -23
  139. package/chunks/{initializer-P5AXF7PV.js → initializer-FJTACLOH.js} +31 -28
  140. package/chunks/{ja-FKZ2WZ4D.js → ja-7DEUB2TS.js} +1 -0
  141. package/chunks/{keychain-token-storage-CSYMNRPP.js → keychain-token-storage-YLMDLKXT.js} +2 -2
  142. package/chunks/{list-6COK34BP.js → list-SCQXYJUW.js} +32 -29
  143. package/chunks/{loadedSettingsAdapter-XIUTN245.js → loadedSettingsAdapter-P6KPNAG7.js} +30 -27
  144. package/chunks/{loop-wakeup-33GPXJLA.js → loop-wakeup-FYEQEJUA.js} +7 -7
  145. package/chunks/{ls-BYQY7RVV.js → ls-R5WP4BGZ.js} +4 -4
  146. package/chunks/{lsp-X54RO4IV.js → lsp-GSICJMMW.js} +2 -2
  147. package/chunks/{monitor-DH7KFSJV.js → monitor-JU3HHHVY.js} +24 -23
  148. package/chunks/nonInteractiveCli-IZEKUHU3.js +100 -0
  149. package/chunks/{notebook-edit-D4R4ZLMA.js → notebook-edit-YHY3Z2QQ.js} +52 -23
  150. package/chunks/{openaiContentGenerator-F74IPQ6E.js → openaiContentGenerator-4WUVA3Q2.js} +13 -12
  151. package/chunks/{pt-SY5NCFGR.js → pt-TNZRNLQB.js} +1 -0
  152. package/chunks/{qwenContentGenerator-ATTK5P4D.js → qwenContentGenerator-MVYYR44B.js} +26 -25
  153. package/chunks/{qwenOAuth2-K4AASPCV.js → qwenOAuth2-6HNGHALT.js} +5 -4
  154. package/chunks/{read-file-767G23OY.js → read-file-NABEO2X4.js} +8 -8
  155. package/chunks/{read-mcp-resource-I5HCZJ6X.js → read-mcp-resource-DQQW7H7T.js} +2 -2
  156. package/chunks/{ripGrep-4OMMGSWX.js → ripGrep-YXLK6UTX.js} +24 -23
  157. package/chunks/{ru-FWNXJG4F.js → ru-NIAUBKCN.js} +1 -0
  158. package/chunks/{run-qwen-serve-REKSTYRA.js → run-qwen-serve-4B5QBMLQ.js} +144 -41
  159. package/chunks/{scheduler-UNTFDQLG.js → scheduler-XAKIYBDL.js} +24 -23
  160. package/chunks/{send-message-2DAFXEYP.js → send-message-EE4TWKVU.js} +2 -2
  161. package/chunks/{serve-KZ56QB5S.js → serve-4RXVLUKD.js} +30 -28
  162. package/chunks/{server-BV5TZUMU.js → server-7PA5QGIA.js} +11037 -8747
  163. package/chunks/{session-OIBXFSGD.js → session-WNVWRYAH.js} +194 -57
  164. package/chunks/{settings-WJTCBIRA.js → settings-YJH32TFF.js} +30 -27
  165. package/chunks/{shell-WC52IBHW.js → shell-YFLABOS5.js} +24 -23
  166. package/chunks/{skill-U7F7JR4M.js → skill-N3J7FUAY.js} +38 -11
  167. package/chunks/{spawnChannel-EI2E5BCW.js → spawnChannel-D6WVMFZJ.js} +26 -25
  168. package/chunks/{src-4F6FBZFI.js → src-IU5GDBEH.js} +88 -28
  169. package/chunks/{startInteractiveUI-SFRVB3CE.js → startInteractiveUI-QIPQTRT4.js} +2784 -2474
  170. package/chunks/{syntheticOutput-PPS3HDVA.js → syntheticOutput-T5YHQ6S2.js} +3 -3
  171. package/chunks/{task-create-OUYCA553.js → task-create-BLY7RHXX.js} +6 -6
  172. package/chunks/{task-list-WAX7I2IA.js → task-list-3QUYNVYR.js} +5 -5
  173. package/chunks/{task-stop-CAIL6FNH.js → task-stop-U4RATK2V.js} +2 -2
  174. package/chunks/{task-update-AHEO5OUT.js → task-update-U54O7KJX.js} +6 -6
  175. package/chunks/{team-create-3WC7CIIW.js → team-create-TP4CLLM3.js} +24 -23
  176. package/chunks/{team-delete-HGHFG23B.js → team-delete-LKBYPASD.js} +5 -5
  177. package/chunks/{theme-manager-DICFCT3P.js → theme-manager-N2TBIBZT.js} +25 -24
  178. package/chunks/{todoWrite-IFBMN3FH.js → todoWrite-OYXKTVYN.js} +4 -4
  179. package/chunks/{tool-search-L5K7HLER.js → tool-search-GBBSICYB.js} +101 -16
  180. package/chunks/trustedFolders-F7TBU5CD.js +76 -0
  181. package/chunks/types-QX5C3CHJ.js +12 -0
  182. package/chunks/{validateNonInterActiveAuth-GCH4Z6KI.js → validateNonInterActiveAuth-EQDQPSMI.js} +55 -50
  183. package/chunks/{web-fetch-B5IIQ5TT.js → web-fetch-PCVCML2X.js} +29 -15
  184. package/chunks/{workflow-OB3AND7I.js → workflow-ANJ4FYBK.js} +32 -30
  185. package/chunks/{workspace-providers-status-ZIR5EPOO.js → workspace-providers-status-462QQKQM.js} +32 -29
  186. package/chunks/workspace-service-VTG4G6CW.js +75 -0
  187. package/chunks/{write-file-SEXBNDZM.js → write-file-3P3WMQQW.js} +63 -27
  188. package/chunks/{zh-RBEGQVQT.js → zh-5EUYXASN.js} +8 -0
  189. package/chunks/{zh-TW-QGAVRXT3.js → zh-TW-TR3XYGE5.js} +8 -0
  190. package/cli-entry.js +20 -9
  191. package/cli.js +6 -6
  192. package/locales/ca.js +2 -0
  193. package/locales/de.js +2 -0
  194. package/locales/en.js +14 -0
  195. package/locales/fr.js +2 -0
  196. package/locales/ja.js +1 -0
  197. package/locales/pt.js +2 -0
  198. package/locales/ru.js +2 -0
  199. package/locales/zh-TW.js +12 -0
  200. package/locales/zh.js +12 -0
  201. package/node_modules/@qwen-code/audio-capture/dist/index.d.ts +35 -0
  202. package/node_modules/@qwen-code/audio-capture/dist/index.js +48 -0
  203. package/node_modules/@qwen-code/audio-capture/dist/index.js.map +1 -0
  204. package/node_modules/@qwen-code/audio-capture/dist/platform.d.ts +7 -0
  205. package/node_modules/@qwen-code/audio-capture/dist/platform.js +18 -0
  206. package/node_modules/@qwen-code/audio-capture/dist/platform.js.map +1 -0
  207. package/node_modules/@qwen-code/audio-capture/node_modules/node-gyp-build/LICENSE +21 -0
  208. package/node_modules/@qwen-code/audio-capture/node_modules/node-gyp-build/README.md +58 -0
  209. package/node_modules/@qwen-code/audio-capture/node_modules/node-gyp-build/SECURITY.md +5 -0
  210. package/node_modules/@qwen-code/audio-capture/node_modules/node-gyp-build/bin.js +84 -0
  211. package/node_modules/@qwen-code/audio-capture/node_modules/node-gyp-build/build-test.js +19 -0
  212. package/node_modules/@qwen-code/audio-capture/node_modules/node-gyp-build/index.js +6 -0
  213. package/node_modules/@qwen-code/audio-capture/node_modules/node-gyp-build/node-gyp-build.js +207 -0
  214. package/node_modules/@qwen-code/audio-capture/node_modules/node-gyp-build/optional.js +7 -0
  215. package/node_modules/@qwen-code/audio-capture/node_modules/node-gyp-build/package.json +43 -0
  216. package/node_modules/@qwen-code/audio-capture/package.json +30 -0
  217. package/node_modules/@qwen-code/audio-capture/prebuilds/darwin-arm64/@qwen-code+audio-capture.node +0 -0
  218. package/node_modules/@qwen-code/audio-capture/prebuilds/darwin-x64/@qwen-code+audio-capture.node +0 -0
  219. package/node_modules/@qwen-code/audio-capture/prebuilds/linux-arm64/@qwen-code+audio-capture.node +0 -0
  220. package/node_modules/@qwen-code/audio-capture/prebuilds/linux-x64/@qwen-code+audio-capture.node +0 -0
  221. package/node_modules/@qwen-code/audio-capture/prebuilds/win32-x64/@qwen-code+audio-capture.node +0 -0
  222. package/package.json +18 -6
  223. package/patches/chrome-devtools-mcp+1.4.0.patch +31 -0
  224. package/postinstall.js +50 -0
  225. package/web-shell/assets/{arc-D_ae_Y9k.js → arc-DIemsO_F.js} +1 -1
  226. package/web-shell/assets/{architectureDiagram-3BPJPVTR-BMLRjppa.js → architectureDiagram-3BPJPVTR-CTlUvIQv.js} +1 -1
  227. package/web-shell/assets/{blockDiagram-GPEHLZMM-qVDQC8KL.js → blockDiagram-GPEHLZMM-DG0jmIZK.js} +1 -1
  228. package/web-shell/assets/{c4Diagram-AAUBKEIU-CxWS6MBd.js → c4Diagram-AAUBKEIU-WljDBi55.js} +1 -1
  229. package/web-shell/assets/channel-CbzRbdpA.js +1 -0
  230. package/web-shell/assets/{chunk-2J33WTMH-BtNuY5Yp.js → chunk-2J33WTMH-8nc6onr2.js} +1 -1
  231. package/web-shell/assets/{chunk-4BX2VUAB-BU-x368d.js → chunk-4BX2VUAB-EopXix8Z.js} +1 -1
  232. package/web-shell/assets/{chunk-55IACEB6-DRgTLvBu.js → chunk-55IACEB6-BE4Jz3N3.js} +1 -1
  233. package/web-shell/assets/{chunk-727SXJPM-D2aElv7q.js → chunk-727SXJPM-C_eErgDN.js} +1 -1
  234. package/web-shell/assets/{chunk-AQP2D5EJ-BwHCMRQ0.js → chunk-AQP2D5EJ-DFtVW-Is.js} +1 -1
  235. package/web-shell/assets/{chunk-FMBD7UC4-BMyPH-ku.js → chunk-FMBD7UC4-CJPyxIg9.js} +1 -1
  236. package/web-shell/assets/{chunk-ND2GUHAM-BNKTstUf.js → chunk-ND2GUHAM-Bk-xgj6M.js} +1 -1
  237. package/web-shell/assets/{chunk-QZHKN3VN-gD3vsFId.js → chunk-QZHKN3VN-p3Y4yaFB.js} +1 -1
  238. package/web-shell/assets/classDiagram-4FO5ZUOK-BFdyQ52h.js +1 -0
  239. package/web-shell/assets/classDiagram-v2-Q7XG4LA2-BFdyQ52h.js +1 -0
  240. package/web-shell/assets/{cose-bilkent-S5V4N54A-BG9FAsXE.js → cose-bilkent-S5V4N54A-BCMO6Js3.js} +1 -1
  241. package/web-shell/assets/{dagre-BM42HDAG-Cy1sR7om.js → dagre-BM42HDAG-DUMrwASf.js} +1 -1
  242. package/web-shell/assets/default-aEtf0G7-.svg +1 -0
  243. package/web-shell/assets/{diagram-2AECGRRQ-Bxi1I_Vu.js → diagram-2AECGRRQ-C5d7qJbL.js} +1 -1
  244. package/web-shell/assets/{diagram-5GNKFQAL-BGCJjO6y.js → diagram-5GNKFQAL-CjLzMhbT.js} +1 -1
  245. package/web-shell/assets/{diagram-KO2AKTUF-CMTeLXp7.js → diagram-KO2AKTUF-RCoNWeAm.js} +1 -1
  246. package/web-shell/assets/{diagram-LMA3HP47-BN1wyAGh.js → diagram-LMA3HP47-KThXjaNV.js} +1 -1
  247. package/web-shell/assets/{diagram-OG6HWLK6-UYSG_zcp.js → diagram-OG6HWLK6-DKsC2BWz.js} +1 -1
  248. package/web-shell/assets/{erDiagram-TEJ5UH35-DqXL08lw.js → erDiagram-TEJ5UH35-DJ2ukmq0.js} +1 -1
  249. package/web-shell/assets/{flowDiagram-I6XJVG4X-BPJ9SFji.js → flowDiagram-I6XJVG4X-zzqaARRX.js} +1 -1
  250. package/web-shell/assets/{ganttDiagram-6RSMTGT7-BEd9Pnms.js → ganttDiagram-6RSMTGT7-kFUg-gqT.js} +1 -1
  251. package/web-shell/assets/{gitGraphDiagram-PVQCEYII-DrNRH3oi.js → gitGraphDiagram-PVQCEYII-C6JnMVzD.js} +1 -1
  252. package/web-shell/assets/index-B3XUm7dw.js +713 -0
  253. package/web-shell/assets/index-CHmzAWys.css +5 -0
  254. package/web-shell/assets/{infoDiagram-5YYISTIA-CFYBrkVI.js → infoDiagram-5YYISTIA-XqPVhp89.js} +1 -1
  255. package/web-shell/assets/insert-DmRbh9Rv.svg +1 -0
  256. package/web-shell/assets/{ishikawaDiagram-YF4QCWOH-Dxsddth7.js → ishikawaDiagram-YF4QCWOH-D2k8sGpf.js} +1 -1
  257. package/web-shell/assets/{journeyDiagram-JHISSGLW-DxaDK80t.js → journeyDiagram-JHISSGLW-CQEST1GE.js} +1 -1
  258. package/web-shell/assets/{kanban-definition-UN3LZRKU-Q0jQCOct.js → kanban-definition-UN3LZRKU-DZ7kqVBC.js} +1 -1
  259. package/web-shell/assets/{linear-DGGM2kcP.js → linear-B6C_6q_E.js} +1 -1
  260. package/web-shell/assets/{mermaid.core-ByoxnWut.js → mermaid.core-CsyNP1qt.js} +5 -5
  261. package/web-shell/assets/{mindmap-definition-RKZ34NQL-C0ZseHsi.js → mindmap-definition-RKZ34NQL-B1VDIzJq.js} +1 -1
  262. package/web-shell/assets/{pieDiagram-4H26LBE5-MQXNhl5A.js → pieDiagram-4H26LBE5-DN0E7Jwu.js} +1 -1
  263. package/web-shell/assets/{quadrantDiagram-W4KKPZXB-DIR4ee2A.js → quadrantDiagram-W4KKPZXB-UKTQdHKm.js} +1 -1
  264. package/web-shell/assets/queue-DWpnpFgW.svg +1 -0
  265. package/web-shell/assets/{requirementDiagram-4Y6WPE33-BTR0UdYQ.js → requirementDiagram-4Y6WPE33-BRGceQvN.js} +1 -1
  266. package/web-shell/assets/{sankeyDiagram-5OEKKPKP-sfnhMELN.js → sankeyDiagram-5OEKKPKP-2DDyu41v.js} +1 -1
  267. package/web-shell/assets/{sequenceDiagram-3UESZ5HK-DXczHOHH.js → sequenceDiagram-3UESZ5HK-CT5iE8ZU.js} +1 -1
  268. package/web-shell/assets/{stateDiagram-AJRCARHV-DdE_KQFH.js → stateDiagram-AJRCARHV-CqMQ_s4G.js} +1 -1
  269. package/web-shell/assets/stateDiagram-v2-BHNVJYJU-CamhV5pM.js +1 -0
  270. package/web-shell/assets/{timeline-definition-PNZ67QCA-DIc61uE8.js → timeline-definition-PNZ67QCA-Di37QSZU.js} +1 -1
  271. package/web-shell/assets/{vennDiagram-CIIHVFJN-BF7ODmxB.js → vennDiagram-CIIHVFJN-DCXvpz5S.js} +1 -1
  272. package/web-shell/assets/{wardley-L42UT6IY-3pc1MEaw.js → wardley-L42UT6IY-CpUCk6OX.js} +1 -1
  273. package/web-shell/assets/{wardleyDiagram-YWT4CUSO-9jnPcZuP.js → wardleyDiagram-YWT4CUSO-D01pVEBg.js} +1 -1
  274. package/web-shell/assets/{xychartDiagram-2RQKCTM6-DcthaZK6.js → xychartDiagram-2RQKCTM6-DOu5JDoz.js} +1 -1
  275. package/web-shell/index.html +2 -2
  276. package/chunks/chunk-B6HQGTYR.js +0 -410
  277. package/chunks/chunk-R2BXK7SX.js +0 -13
  278. package/chunks/chunk-RKIZXJMB.js +0 -13
  279. package/chunks/nonInteractiveCli-7575LBXP.js +0 -91
  280. package/chunks/workspace-service-OM5D4OI2.js +0 -60
  281. package/web-shell/assets/channel-DYPNiTww.js +0 -1
  282. package/web-shell/assets/classDiagram-4FO5ZUOK-p_g25Tni.js +0 -1
  283. package/web-shell/assets/classDiagram-v2-Q7XG4LA2-p_g25Tni.js +0 -1
  284. package/web-shell/assets/index-CpOXWDPh.js +0 -714
  285. package/web-shell/assets/index-oVK7qhre.css +0 -5
  286. package/web-shell/assets/stateDiagram-v2-BHNVJYJU-BHUSEndU.js +0 -1
@@ -0,0 +1,4923 @@
1
+ // Force strict mode and setup for ESM
2
+ "use strict";
3
+ import {
4
+ SUPPORTED_LANGUAGES,
5
+ detectSystemLanguage,
6
+ getLanguageNameFromLocale,
7
+ getLanguageSettingsOptions,
8
+ t
9
+ } from "./chunk-MTHMXSNT.js";
10
+ import {
11
+ DEFAULT_DARK_THEME_NAME,
12
+ DEFAULT_LIGHT_THEME_NAME
13
+ } from "./chunk-TLRYABYP.js";
14
+ import {
15
+ CONSOLIDATED_DISABLE_KEYS,
16
+ DEFAULT_EXCLUDED_ENV_VARS,
17
+ ENV_CORRUPTED_PATH,
18
+ ENV_WAS_RECOVERED,
19
+ HOME_ENV_BOOTSTRAP_KEYS,
20
+ PROJECT_ENV_HARDCODED_EXCLUSIONS,
21
+ V1_INDICATOR_KEYS,
22
+ V1_TO_V2_MIGRATION_MAP,
23
+ V1_TO_V2_PRESERVE_DISABLE_MAP,
24
+ V2_CONTAINER_KEYS,
25
+ getSystemDefaultsPath,
26
+ getSystemSettingsPath,
27
+ resolveEnvVarsInObject
28
+ } from "./chunk-PPFWEV7G.js";
29
+ import {
30
+ isWorkspaceTrusted,
31
+ updateSettingsFilePreservingFormat
32
+ } from "./chunk-ARYCHZ4B.js";
33
+ import {
34
+ require_strip_json_comments
35
+ } from "./chunk-QYUE6W3T.js";
36
+ import {
37
+ DEFAULT_STOP_HOOK_BLOCK_CAP,
38
+ DEFAULT_TOOL_OUTPUT_BATCH_BUDGET,
39
+ DEFAULT_TOOL_RESULTS_TOTAL_CHARS_THRESHOLD,
40
+ DEFAULT_TRUNCATE_TOOL_OUTPUT_LINES,
41
+ DEFAULT_TRUNCATE_TOOL_OUTPUT_THRESHOLD,
42
+ stripRuntimeSnapshotPrefix
43
+ } from "./chunk-RIP623NO.js";
44
+ import {
45
+ require_main
46
+ } from "./chunk-SKAZWEV5.js";
47
+ import {
48
+ DEFAULT_QWEN_CUSTOM_IGNORE_FILE_NAMES
49
+ } from "./chunk-JOLIREYR.js";
50
+ import {
51
+ DEFAULT_SENSITIVE_SPAN_ATTRIBUTE_MAX_LENGTH,
52
+ SENSITIVE_SPAN_ATTRIBUTE_MAX_LENGTH_LIMIT
53
+ } from "./chunk-SS6WJMQK.js";
54
+ import {
55
+ FatalConfigError,
56
+ QWEN_DIR,
57
+ Storage,
58
+ createDebugLogger,
59
+ getErrorMessage
60
+ } from "./chunk-UX4SGCGN.js";
61
+ import {
62
+ init_esbuild_shims
63
+ } from "./chunk-A4BMJM77.js";
64
+ import {
65
+ __name,
66
+ __toESM
67
+ } from "./chunk-J2S4EL5Y.js";
68
+
69
+ // packages/cli/src/config/settings.ts
70
+ init_esbuild_shims();
71
+ import * as fs4 from "node:fs";
72
+ import * as path3 from "node:path";
73
+ import { homedir as homedir2 } from "node:os";
74
+ import process2 from "node:process";
75
+ var import_strip_json_comments = __toESM(require_strip_json_comments(), 1);
76
+
77
+ // packages/cli/src/config/modelProvidersScope.ts
78
+ init_esbuild_shims();
79
+ function hasOwnModelProviders(settingsObj) {
80
+ if (!settingsObj || typeof settingsObj !== "object") {
81
+ return false;
82
+ }
83
+ const obj = settingsObj;
84
+ return Object.prototype.hasOwnProperty.call(obj, "modelProviders");
85
+ }
86
+ __name(hasOwnModelProviders, "hasOwnModelProviders");
87
+ function getModelProvidersOwnerScope(settings) {
88
+ if (settings.isTrusted && hasOwnModelProviders(settings.workspace.settings)) {
89
+ return "Workspace" /* Workspace */;
90
+ }
91
+ if (hasOwnModelProviders(settings.user.settings)) {
92
+ return "User" /* User */;
93
+ }
94
+ return void 0;
95
+ }
96
+ __name(getModelProvidersOwnerScope, "getModelProvidersOwnerScope");
97
+ function getPersistScopeForModelSelection(settings) {
98
+ return getModelProvidersOwnerScope(settings) ?? "User" /* User */;
99
+ }
100
+ __name(getPersistScopeForModelSelection, "getPersistScopeForModelSelection");
101
+
102
+ // packages/cli/src/config/settingsSchema.ts
103
+ init_esbuild_shims();
104
+ var TOGGLE_TYPES = /* @__PURE__ */ new Set([
105
+ "boolean",
106
+ "enum"
107
+ ]);
108
+ var HOOK_DEFINITION_ITEMS = {
109
+ type: "object",
110
+ description: "A hook definition with an optional matcher and a list of hook configurations.",
111
+ properties: {
112
+ matcher: {
113
+ type: "string",
114
+ description: "An optional matcher pattern to filter when this hook definition applies."
115
+ },
116
+ sequential: {
117
+ type: "boolean",
118
+ description: "Whether the hooks should be executed sequentially instead of in parallel."
119
+ },
120
+ hooks: {
121
+ type: "array",
122
+ description: "The list of hook configurations to execute.",
123
+ required: true,
124
+ items: {
125
+ type: "object",
126
+ description: "A hook configuration entry that defines a hook to execute.",
127
+ properties: {
128
+ type: {
129
+ type: "string",
130
+ description: 'The type of hook. Note: "function" type is only available via SDK registration, not settings.json.',
131
+ enum: ["command", "http"],
132
+ required: true
133
+ },
134
+ command: {
135
+ type: "string",
136
+ description: 'The command to execute when the hook is triggered. Required for "command" type.'
137
+ },
138
+ url: {
139
+ type: "string",
140
+ description: 'The URL to send the POST request to. Required for "http" type.'
141
+ },
142
+ headers: {
143
+ type: "object",
144
+ description: "HTTP headers to include in the request. Supports env var interpolation ($VAR, ${VAR}).",
145
+ additionalProperties: { type: "string" }
146
+ },
147
+ allowedEnvVars: {
148
+ type: "array",
149
+ description: "List of environment variables allowed for interpolation in headers and URL.",
150
+ items: { type: "string" }
151
+ },
152
+ name: {
153
+ type: "string",
154
+ description: "An optional name for the hook."
155
+ },
156
+ description: {
157
+ type: "string",
158
+ description: "An optional description of what the hook does."
159
+ },
160
+ timeout: {
161
+ type: "number",
162
+ description: "Timeout in seconds for the hook execution."
163
+ },
164
+ env: {
165
+ type: "object",
166
+ description: "Environment variables to set when executing the hook command.",
167
+ additionalProperties: { type: "string" }
168
+ },
169
+ async: {
170
+ type: "boolean",
171
+ description: 'Whether to execute the hook asynchronously (non-blocking, for "command" type only).'
172
+ },
173
+ once: {
174
+ type: "boolean",
175
+ description: 'Whether to execute the hook only once per session (for "http" type).'
176
+ },
177
+ statusMessage: {
178
+ type: "string",
179
+ description: "A message to display while the hook is executing."
180
+ },
181
+ shell: {
182
+ type: "string",
183
+ description: "The shell to use for command execution.",
184
+ enum: ["bash", "powershell"]
185
+ }
186
+ }
187
+ }
188
+ }
189
+ }
190
+ };
191
+ var SETTINGS_SCHEMA = {
192
+ // Maintained for compatibility/criticality
193
+ mcpServers: {
194
+ type: "object",
195
+ label: "MCP Servers",
196
+ category: "Advanced",
197
+ // Hot-reloadable (sub-task 3): a runtime edit is reconciled live by the
198
+ // SettingsWatcher → reinitializeMcpServers path. Marking it
199
+ // restart-required would make the watcher suppress MCP-only edits, so the
200
+ // listener that drives the reconcile would never fire.
201
+ requiresRestart: false,
202
+ default: {},
203
+ description: "Configuration for MCP servers.",
204
+ showInDialog: false,
205
+ mergeStrategy: "shallow_merge" /* SHALLOW_MERGE */
206
+ },
207
+ // Channels configuration (Telegram, Discord, etc.)
208
+ channels: {
209
+ type: "object",
210
+ label: "Channels",
211
+ category: "Advanced",
212
+ requiresRestart: true,
213
+ default: {},
214
+ description: "Configuration for messaging channels.",
215
+ showInDialog: false,
216
+ mergeStrategy: "shallow_merge" /* SHALLOW_MERGE */
217
+ },
218
+ // Model providers configuration grouped by authType
219
+ modelProviders: {
220
+ type: "object",
221
+ label: "Model Providers",
222
+ category: "Model",
223
+ requiresRestart: false,
224
+ default: {},
225
+ description: 'Model providers configuration keyed by provider id (a built-in AuthType such as "openai" or "gemini", or a custom id mapped via providerProtocol). Each entry is an array of model configurations.',
226
+ showInDialog: false,
227
+ mergeStrategy: "replace" /* REPLACE */
228
+ },
229
+ // Maps a custom modelProviders provider id to its SDK protocol (AuthType).
230
+ providerProtocol: {
231
+ type: "object",
232
+ label: "Provider Protocols",
233
+ category: "Model",
234
+ requiresRestart: true,
235
+ default: {},
236
+ description: 'Maps a custom modelProviders provider id to the SDK protocol that routes its requests (e.g. {"idealab": "openai"}). Lets a custom provider id reuse a built-in protocol. Built-in provider ids (openai, gemini, anthropic, vertex-ai, qwen-oauth) are routed automatically and need no entry.',
237
+ showInDialog: false,
238
+ mergeStrategy: "replace" /* REPLACE */
239
+ },
240
+ plansDirectory: {
241
+ type: "string",
242
+ label: "Plans Directory",
243
+ category: "Advanced",
244
+ requiresRestart: true,
245
+ default: void 0,
246
+ description: "Custom directory for approved Plan Mode files. Relative paths are resolved from the project root, and the resolved path must stay within the project root. Defaults to ~/.qwen/plans.",
247
+ showInDialog: false
248
+ },
249
+ // Environment variables fallback
250
+ env: {
251
+ type: "object",
252
+ label: "Environment Variables",
253
+ category: "Advanced",
254
+ requiresRestart: true,
255
+ default: {},
256
+ description: "Environment variables to set as fallback defaults. These are loaded with the lowest priority: system environment variables > .env files > settings.json env field.",
257
+ showInDialog: false,
258
+ mergeStrategy: "shallow_merge" /* SHALLOW_MERGE */
259
+ },
260
+ proxy: {
261
+ type: "string",
262
+ label: "Proxy",
263
+ category: "Advanced",
264
+ requiresRestart: true,
265
+ default: void 0,
266
+ description: "Proxy URL for CLI HTTP requests. Takes precedence over proxy environment variables when --proxy is not provided.",
267
+ showInDialog: false
268
+ },
269
+ general: {
270
+ type: "object",
271
+ label: "General",
272
+ category: "General",
273
+ requiresRestart: false,
274
+ default: {},
275
+ description: "General application settings.",
276
+ showInDialog: false,
277
+ properties: {
278
+ preferredEditor: {
279
+ type: "string",
280
+ label: "Preferred Editor",
281
+ category: "General",
282
+ requiresRestart: false,
283
+ default: void 0,
284
+ description: "The preferred editor to open files in.",
285
+ showInDialog: true
286
+ },
287
+ vimMode: {
288
+ type: "boolean",
289
+ label: "Vim Mode",
290
+ category: "General",
291
+ requiresRestart: false,
292
+ default: false,
293
+ description: "Enable Vim keybindings",
294
+ showInDialog: true
295
+ },
296
+ voice: {
297
+ type: "object",
298
+ label: "Voice Dictation",
299
+ category: "General",
300
+ requiresRestart: false,
301
+ default: {},
302
+ description: "Voice dictation settings.",
303
+ showInDialog: false,
304
+ properties: {
305
+ enabled: {
306
+ type: "boolean",
307
+ label: "Voice Dictation",
308
+ category: "General",
309
+ requiresRestart: false,
310
+ default: false,
311
+ description: "Enable voice dictation in the prompt input.",
312
+ showInDialog: false
313
+ },
314
+ mode: {
315
+ type: "enum",
316
+ label: "Voice Dictation Mode",
317
+ category: "General",
318
+ requiresRestart: false,
319
+ default: "hold",
320
+ description: 'How push-to-talk behaves: "hold" to talk while held, or "tap" to start and tap (or pause) to stop and submit.',
321
+ showInDialog: false,
322
+ options: [
323
+ { value: "hold", label: "Hold to talk" },
324
+ { value: "tap", label: "Tap to toggle" }
325
+ ]
326
+ },
327
+ language: {
328
+ type: "string",
329
+ label: "Voice Dictation Language",
330
+ category: "General",
331
+ requiresRestart: false,
332
+ default: "",
333
+ description: 'Preferred spoken language for voice transcription (e.g. "english", "chinese"). Leave empty to auto-detect.',
334
+ showInDialog: false
335
+ },
336
+ keytermsFile: {
337
+ type: "string",
338
+ label: "Voice Dictation Keyterms File",
339
+ category: "General",
340
+ requiresRestart: false,
341
+ default: "",
342
+ description: 'Path to a custom keyterms file (one term per line, "#" for comments) that biases voice transcription toward domain-specific terms. Relative paths resolve from the workspace root; defaults to ".qwen/voice-keyterms.txt" when present. The file contents are sent to the ASR provider and it is read only in trusted workspaces. Only applies to Qwen ASR models (qwen3-asr-*).',
343
+ showInDialog: false
344
+ },
345
+ refineTranscript: {
346
+ type: "boolean",
347
+ label: "Refine Voice Transcript",
348
+ category: "General",
349
+ requiresRestart: false,
350
+ default: true,
351
+ description: "Clean up voice transcripts with the fast model before inserting them \u2014 removes filler words and fixes recognition errors while preserving meaning. Falls back to the raw transcript on failure, and is skipped when no fast model is configured.",
352
+ showInDialog: false
353
+ }
354
+ }
355
+ },
356
+ enableAutoUpdate: {
357
+ type: "boolean",
358
+ label: "Enable Auto Update",
359
+ category: "General",
360
+ requiresRestart: false,
361
+ default: true,
362
+ description: "Enable automatic update checks and installations on startup.",
363
+ showInDialog: true
364
+ },
365
+ showSessionRecap: {
366
+ type: "boolean",
367
+ label: "Show Session Recap",
368
+ category: "General",
369
+ requiresRestart: false,
370
+ // Off by default — an ambient background LLM call isn't something
371
+ // users should be opted into silently, especially when `fastModel`
372
+ // is unset and the call would land on the main coding model.
373
+ // Manual `/recap` works regardless.
374
+ default: false,
375
+ description: 'Auto-show a one-line "where you left off" recap when returning to the terminal after being away. Off by default. Use /recap to trigger manually regardless of this setting.',
376
+ showInDialog: true
377
+ },
378
+ sessionRecapAwayThresholdMinutes: {
379
+ type: "number",
380
+ label: "Session Recap Away Threshold (minutes)",
381
+ category: "General",
382
+ requiresRestart: false,
383
+ default: 5,
384
+ minimum: 1,
385
+ description: "How many minutes the terminal must be blurred before an auto-recap fires on the next focus-in. Matches Claude Code's default of 5 minutes; raise if you briefly alt-tab and do not want recaps to pile up.",
386
+ showInDialog: true
387
+ },
388
+ cleanupPeriodDays: {
389
+ type: "number",
390
+ label: "Cleanup Period (days)",
391
+ category: "General",
392
+ // LoadedSettings._merged is cached without verified setValue→recompute
393
+ // paths in all UI flows. Mark restart-required so users aren't
394
+ // surprised when a mid-session edit doesn't take effect immediately.
395
+ requiresRestart: true,
396
+ default: 30,
397
+ minimum: 0,
398
+ description: "Number of days to retain ~/.qwen/file-history/ session backups used by /rewind and background subagent transcripts under <projectDir>/subagents/. Data older than this is removed by a background housekeeping pass that runs at most once per day. Set to 0 for minimum retention (~1 hour) \u2014 protects sessions touched in the last hour, plus the currently active session.",
399
+ showInDialog: true
400
+ },
401
+ gitCoAuthor: {
402
+ type: "object",
403
+ label: "Attribution",
404
+ category: "General",
405
+ requiresRestart: false,
406
+ // Match `normalizeGitCoAuthor`'s runtime defaults so the IDE
407
+ // schema publishes the same "enabled by default" hint users see
408
+ // at runtime. The empty-object form here would silently lose
409
+ // editor-surfaced defaults.
410
+ default: { commit: true, pr: true },
411
+ description: "Attribution added to git commits and pull requests created through Qwen Code.",
412
+ showInDialog: false,
413
+ // Pre-V4 settings stored this as a single boolean. The V3→V4
414
+ // migration rewrites those on first launch, but the IDE schema
415
+ // validator runs before that — accept the boolean shape so users
416
+ // editing settings.json in VS Code don't see a spurious warning
417
+ // until they run qwen once. Config.normalizeGitCoAuthor handles
418
+ // the boolean at runtime.
419
+ legacyTypes: ["boolean"],
420
+ properties: {
421
+ commit: {
422
+ type: "boolean",
423
+ label: "Attribution: commit",
424
+ category: "General",
425
+ requiresRestart: false,
426
+ default: true,
427
+ description: "Add a Co-authored-by trailer to git commit messages AND attach a per-file AI-attribution git note (`refs/notes/ai-attribution`) for commits made through Qwen Code. Disabling skips both.",
428
+ showInDialog: true
429
+ },
430
+ pr: {
431
+ type: "boolean",
432
+ label: "Attribution: PR",
433
+ category: "General",
434
+ requiresRestart: false,
435
+ default: true,
436
+ description: "Append a Qwen Code attribution line to PR descriptions when running `gh pr create`.",
437
+ showInDialog: true
438
+ }
439
+ }
440
+ },
441
+ debugKeystrokeLogging: {
442
+ type: "boolean",
443
+ label: "Debug Keystroke Logging",
444
+ category: "General",
445
+ requiresRestart: false,
446
+ default: false,
447
+ description: "Enable debug logging of keystrokes to the console.",
448
+ showInDialog: false
449
+ },
450
+ language: {
451
+ type: "enum",
452
+ label: "Language: UI",
453
+ category: "General",
454
+ requiresRestart: true,
455
+ default: "auto",
456
+ description: 'The language for the user interface. Use "auto" to detect from system settings. You can also use custom language codes (e.g., "es", "fr") by placing JS language files in ~/.qwen/locales/ (e.g., ~/.qwen/locales/es.js).',
457
+ showInDialog: true,
458
+ options: []
459
+ },
460
+ outputLanguage: {
461
+ type: "string",
462
+ label: "Language: Model",
463
+ category: "General",
464
+ requiresRestart: true,
465
+ default: "auto",
466
+ description: 'The language for LLM output. Use "auto" to detect from system settings, or set a specific language.',
467
+ showInDialog: true
468
+ },
469
+ dynamicCommandTranslation: {
470
+ type: "boolean",
471
+ label: "Language: Dynamic Command Translation",
472
+ category: "General",
473
+ requiresRestart: false,
474
+ default: false,
475
+ description: "Enable AI translation for dynamic slash command descriptions. When disabled, dynamic commands use their original descriptions and do not trigger translation model calls.",
476
+ showInDialog: true
477
+ },
478
+ terminalBell: {
479
+ type: "boolean",
480
+ label: "Terminal Bell Notification",
481
+ category: "General",
482
+ requiresRestart: false,
483
+ default: true,
484
+ description: "Play terminal bell sound when response completes or needs approval.",
485
+ showInDialog: true
486
+ },
487
+ preventSystemSleep: {
488
+ type: "boolean",
489
+ label: "Prevent System Sleep While Running",
490
+ category: "General",
491
+ // Read once at startup via Config.preventSystemSleep (a readonly field
492
+ // captured in loadCliConfig), so a runtime toggle only takes effect
493
+ // after restart.
494
+ requiresRestart: true,
495
+ default: true,
496
+ description: "Prevent the system from sleeping while Qwen Code is streaming a model response or executing tools. Idle prompt time and permission prompts do not inhibit sleep.",
497
+ showInDialog: true
498
+ },
499
+ chatRecording: {
500
+ type: "boolean",
501
+ label: "Chat Recording",
502
+ category: "General",
503
+ requiresRestart: true,
504
+ default: true,
505
+ description: "Enable saving chat history to disk. Disabling this will also prevent --continue and --resume from working.",
506
+ showInDialog: false
507
+ },
508
+ defaultFileEncoding: {
509
+ type: "enum",
510
+ label: "Default File Encoding",
511
+ category: "General",
512
+ requiresRestart: false,
513
+ default: "utf-8",
514
+ description: 'Default encoding for new files. Use "utf-8" (default) for UTF-8 without BOM, or "utf-8-bom" for UTF-8 with BOM. Only change this if your project specifically requires BOM.',
515
+ showInDialog: false,
516
+ options: [
517
+ { value: "utf-8", label: "UTF-8 (without BOM)" },
518
+ { value: "utf-8-bom", label: "UTF-8 with BOM" }
519
+ ]
520
+ }
521
+ }
522
+ },
523
+ output: {
524
+ type: "object",
525
+ label: "Output",
526
+ category: "General",
527
+ requiresRestart: false,
528
+ default: {},
529
+ description: "Settings for the CLI output.",
530
+ showInDialog: false,
531
+ properties: {
532
+ format: {
533
+ type: "enum",
534
+ label: "Output Format",
535
+ category: "General",
536
+ requiresRestart: false,
537
+ default: "text",
538
+ description: "The format of the CLI output.",
539
+ showInDialog: false,
540
+ options: [
541
+ { value: "text", label: "Text" },
542
+ { value: "json", label: "JSON" }
543
+ ]
544
+ },
545
+ showTimestamps: {
546
+ type: "boolean",
547
+ label: "Show Timestamps",
548
+ category: "General",
549
+ requiresRestart: false,
550
+ default: false,
551
+ description: "Show [HH:MM:SS] timestamp before each assistant response.",
552
+ showInDialog: true
553
+ }
554
+ }
555
+ },
556
+ dualOutput: {
557
+ type: "object",
558
+ label: "Dual Output",
559
+ category: "Advanced",
560
+ requiresRestart: true,
561
+ default: {},
562
+ description: "Dual-output sidecar mode: emit structured JSON events to a second channel while the TUI renders normally on stdout. See docs/users/features/dual-output.md. CLI flags take precedence over these settings.",
563
+ showInDialog: false,
564
+ properties: {
565
+ jsonFile: {
566
+ type: "string",
567
+ label: "JSON Event File",
568
+ category: "Advanced",
569
+ requiresRestart: true,
570
+ default: void 0,
571
+ description: "File path for structured JSON event output. Equivalent to --json-file. Ignored if --json-fd or --json-file is also set.",
572
+ showInDialog: false
573
+ },
574
+ inputFile: {
575
+ type: "string",
576
+ label: "Remote Input File",
577
+ category: "Advanced",
578
+ requiresRestart: true,
579
+ default: void 0,
580
+ description: "File path for remote input commands (JSONL). Equivalent to --input-file. Ignored if --input-file is also set.",
581
+ showInDialog: false
582
+ }
583
+ }
584
+ },
585
+ ui: {
586
+ type: "object",
587
+ label: "UI",
588
+ category: "UI",
589
+ requiresRestart: false,
590
+ default: {},
591
+ description: "User interface settings.",
592
+ showInDialog: false,
593
+ properties: {
594
+ theme: {
595
+ type: "string",
596
+ label: "Theme",
597
+ category: "UI",
598
+ requiresRestart: false,
599
+ default: "Qwen Dark",
600
+ description: "The color theme for the UI.",
601
+ showInDialog: true
602
+ },
603
+ autoModeAcknowledged: {
604
+ type: "boolean",
605
+ label: "Auto Mode Acknowledged",
606
+ category: "UI",
607
+ requiresRestart: false,
608
+ default: false,
609
+ description: "True once the user has seen the first-time information message about the AUTO approval mode. Set automatically; not intended for manual configuration.",
610
+ showInDialog: false
611
+ },
612
+ statusLine: {
613
+ type: "object",
614
+ label: "Status Line",
615
+ category: "UI",
616
+ requiresRestart: false,
617
+ default: void 0,
618
+ description: 'Status line display configuration. Use `type: "preset"` with built-in item ids, or `type: "command"` with a shell command. Optional command `refreshInterval` (seconds, >= 1) re-runs the command on a timer so external data stays fresh. Set `respectUserColors: true` to preserve ANSI color codes in command output instead of applying dim/theme styling. Set `hideContextIndicator: true` to hide the built-in context usage indicator in the footer right section. When unset (default), the built-in default preset (model, git branch, context usage, current dir) is shown automatically; set to `null` to explicitly disable the status line.',
619
+ showInDialog: false
620
+ },
621
+ customThemes: {
622
+ type: "object",
623
+ label: "Custom Themes",
624
+ category: "UI",
625
+ requiresRestart: false,
626
+ default: {},
627
+ description: "Custom theme definitions.",
628
+ showInDialog: false
629
+ },
630
+ hideBuiltinWorktreeIndicator: {
631
+ type: "boolean",
632
+ label: "Hide Built-in Worktree Indicator",
633
+ category: "UI",
634
+ requiresRestart: false,
635
+ default: false,
636
+ description: "When true, the built-in `\u2387 worktree-<branch> (<slug>)` line in the Footer is hidden. The worktree state is still surfaced to custom statusline scripts via the stdin payload (`worktree.{name, path, branch, original_cwd, original_branch}`). Keep at the default `false` unless your custom statusline renders the worktree itself \u2014 otherwise an active worktree silently has no UI affordance.",
637
+ showInDialog: false
638
+ },
639
+ hideWindowTitle: {
640
+ type: "boolean",
641
+ label: "Hide Window Title",
642
+ category: "UI",
643
+ requiresRestart: true,
644
+ default: false,
645
+ description: "Hide the window title bar",
646
+ showInDialog: false
647
+ },
648
+ disableWorkflowKeywordTrigger: {
649
+ type: "boolean",
650
+ label: "Disable Workflow Keyword Trigger",
651
+ category: "UI",
652
+ requiresRestart: false,
653
+ default: false,
654
+ description: "When true, mentioning the word `workflow` in a prompt no longer softly steers the turn toward the Workflow tool (and the Footer `workflow active` indicator is suppressed). Only applies when workflows are enabled.",
655
+ showInDialog: true
656
+ },
657
+ showStatusInTitle: {
658
+ type: "boolean",
659
+ label: "Show Status in Title",
660
+ category: "UI",
661
+ requiresRestart: false,
662
+ default: true,
663
+ description: "Show Qwen Code session name and status in the terminal window title",
664
+ showInDialog: true
665
+ },
666
+ hideTips: {
667
+ type: "boolean",
668
+ label: "Hide Tips",
669
+ category: "UI",
670
+ requiresRestart: false,
671
+ default: false,
672
+ description: "Hide helpful tips in the UI",
673
+ showInDialog: true
674
+ },
675
+ history: {
676
+ type: "object",
677
+ label: "History",
678
+ category: "UI",
679
+ requiresRestart: false,
680
+ default: {},
681
+ description: "History display settings.",
682
+ showInDialog: false,
683
+ properties: {
684
+ collapseOnResume: {
685
+ type: "boolean",
686
+ label: "Collapse On Resume",
687
+ category: "UI",
688
+ requiresRestart: false,
689
+ default: false,
690
+ description: "Whether to collapse history by default when resuming a session.",
691
+ showInDialog: false
692
+ }
693
+ }
694
+ },
695
+ showLineNumbers: {
696
+ type: "boolean",
697
+ label: "Show Line Numbers in Code",
698
+ category: "UI",
699
+ requiresRestart: false,
700
+ default: true,
701
+ description: "Show line numbers in the code output.",
702
+ showInDialog: true
703
+ },
704
+ renderMode: {
705
+ type: "enum",
706
+ label: "Markdown Render Mode",
707
+ category: "UI",
708
+ requiresRestart: false,
709
+ default: "render",
710
+ description: 'Default Markdown display mode. Use "render" for rich visual previews, or "raw" to show source-oriented Markdown by default. Toggle during a session with Alt/Option+M; on macOS the terminal must send Option as Meta.',
711
+ showInDialog: true,
712
+ options: [
713
+ { value: "render", label: "Render visual previews" },
714
+ { value: "raw", label: "Show raw source" }
715
+ ]
716
+ },
717
+ showCitations: {
718
+ type: "boolean",
719
+ label: "Show Citations",
720
+ category: "UI",
721
+ requiresRestart: false,
722
+ default: false,
723
+ description: "Show citations for generated text in the chat.",
724
+ showInDialog: false
725
+ },
726
+ customWittyPhrases: {
727
+ type: "array",
728
+ label: "Custom Witty Phrases",
729
+ category: "UI",
730
+ requiresRestart: false,
731
+ default: [],
732
+ description: "Custom witty phrases to display during loading.",
733
+ showInDialog: false
734
+ },
735
+ showResponseTokensPerSecond: {
736
+ type: "boolean",
737
+ label: "Show Response Tokens Per Second",
738
+ category: "UI",
739
+ requiresRestart: true,
740
+ default: false,
741
+ description: "Show a live tokens/sec estimate next to the response token counter while the model is streaming. Takes effect in the next session.",
742
+ showInDialog: true
743
+ },
744
+ enableWelcomeBack: {
745
+ type: "boolean",
746
+ label: "Show Welcome Back Dialog",
747
+ category: "UI",
748
+ requiresRestart: false,
749
+ default: true,
750
+ description: 'Show welcome back dialog when returning to a project with conversation history. Choosing "Start new chat session" suppresses the dialog for that project until the project summary changes.',
751
+ showInDialog: true
752
+ },
753
+ enableUserFeedback: {
754
+ type: "boolean",
755
+ label: "Enable User Feedback",
756
+ category: "UI",
757
+ requiresRestart: false,
758
+ default: true,
759
+ description: "Show optional feedback dialog after conversations to help improve Qwen performance.",
760
+ showInDialog: true
761
+ },
762
+ enableFollowupSuggestions: {
763
+ type: "boolean",
764
+ label: "Enable Follow-up Suggestions",
765
+ category: "UI",
766
+ requiresRestart: false,
767
+ default: true,
768
+ description: "Show context-aware follow-up suggestions after task completion. Press Tab, Right Arrow, or Enter to accept into the input buffer.",
769
+ showInDialog: true
770
+ },
771
+ enableCacheSharing: {
772
+ type: "boolean",
773
+ label: "Enable Cache Sharing for Suggestions",
774
+ category: "UI",
775
+ requiresRestart: false,
776
+ default: true,
777
+ description: "Use cache-aware forked queries for suggestion generation. Reduces cost on providers that support prefix caching (experimental).",
778
+ showInDialog: false
779
+ },
780
+ enableSpeculation: {
781
+ type: "boolean",
782
+ label: "Enable Speculative Execution",
783
+ category: "UI",
784
+ requiresRestart: false,
785
+ default: false,
786
+ description: "Speculatively execute accepted suggestions before submission. Results appear instantly when you accept (experimental).",
787
+ showInDialog: false
788
+ },
789
+ accessibility: {
790
+ type: "object",
791
+ label: "Accessibility",
792
+ category: "UI",
793
+ requiresRestart: true,
794
+ default: {},
795
+ description: "Accessibility settings.",
796
+ showInDialog: false,
797
+ properties: {
798
+ enableLoadingPhrases: {
799
+ type: "boolean",
800
+ label: "Enable Loading Phrases",
801
+ category: "UI",
802
+ requiresRestart: true,
803
+ default: true,
804
+ description: "Enable loading phrases (disable for accessibility)",
805
+ showInDialog: true
806
+ },
807
+ screenReader: {
808
+ type: "boolean",
809
+ label: "Screen Reader Mode",
810
+ category: "UI",
811
+ requiresRestart: true,
812
+ default: void 0,
813
+ description: "Render output in plain-text to be more screen reader accessible",
814
+ showInDialog: false
815
+ }
816
+ }
817
+ },
818
+ feedbackLastShownTimestamp: {
819
+ type: "number",
820
+ label: "Feedback Last Shown Timestamp",
821
+ category: "UI",
822
+ requiresRestart: false,
823
+ default: 0,
824
+ description: "The last time the feedback dialog was shown.",
825
+ showInDialog: false
826
+ },
827
+ compactMode: {
828
+ type: "boolean",
829
+ label: "Compact Mode",
830
+ category: "UI",
831
+ requiresRestart: false,
832
+ default: false,
833
+ description: "Hide tool output and thinking for a cleaner view (toggle with Ctrl+O).",
834
+ showInDialog: true
835
+ },
836
+ compactInline: {
837
+ type: "boolean",
838
+ label: "Compact Inline",
839
+ category: "UI",
840
+ requiresRestart: true,
841
+ default: false,
842
+ description: "Compact tool display within each group instead of merging across groups. Requires compactMode to be enabled.",
843
+ showInDialog: true
844
+ },
845
+ useTerminalBuffer: {
846
+ type: "boolean",
847
+ label: "Virtualized History (reduces flicker on long sessions)",
848
+ category: "UI",
849
+ requiresRestart: false,
850
+ default: false,
851
+ description: "Render conversation history in an in-app scrollable viewport instead of the terminal scrollback buffer. Recommended if you see flicker, scroll-storm, or interface freeze on long sessions, after Ctrl+O, after Ctrl+E / Ctrl+F (expand), after window resize, or when alt-tabbing back. Scroll with Shift+\u2191/\u2193 (line), PgUp/PgDn (page), Ctrl+Home/End (top/bottom), or the mouse wheel. Does NOT use the host terminal scrollback while enabled; for native text selection, hold Shift (or Option on macOS) while dragging.",
852
+ showInDialog: true
853
+ },
854
+ shellOutputMaxLines: {
855
+ type: "number",
856
+ label: "Shell Output Max Lines",
857
+ category: "UI",
858
+ requiresRestart: false,
859
+ default: 5,
860
+ description: "Max number of shell output lines shown inline. Set to 0 to disable the cap and show full output. The hidden line count is still surfaced via the `+N lines` indicator.",
861
+ showInDialog: true
862
+ },
863
+ hideBanner: {
864
+ type: "boolean",
865
+ label: "Hide Banner",
866
+ category: "UI",
867
+ requiresRestart: false,
868
+ default: false,
869
+ description: "Hide the startup ASCII banner and info panel.",
870
+ showInDialog: true
871
+ },
872
+ customBannerTitle: {
873
+ type: "string",
874
+ label: "Custom Banner Title",
875
+ category: "UI",
876
+ requiresRestart: false,
877
+ default: "",
878
+ description: 'Replace the default ">_ Qwen Code" title shown in the banner info panel. The version suffix is always appended.',
879
+ showInDialog: false
880
+ },
881
+ customBannerSubtitle: {
882
+ type: "string",
883
+ label: "Custom Banner Subtitle",
884
+ category: "UI",
885
+ requiresRestart: false,
886
+ default: "",
887
+ description: "Optional subtitle line rendered between the banner title and the auth/model line. When unset, the info panel keeps its blank spacer row.",
888
+ showInDialog: false
889
+ },
890
+ customAsciiArt: {
891
+ type: "object",
892
+ label: "Custom ASCII Art",
893
+ category: "UI",
894
+ requiresRestart: false,
895
+ default: void 0,
896
+ description: 'Replace the default QWEN ASCII art. Accepts an inline string, {"path": "..."}, or {"small": ..., "large": ...} for width-aware selection.',
897
+ showInDialog: false,
898
+ // The runtime accepts three shapes (inline string, {path}, or
899
+ // {small,large} where each tier is itself string-or-{path}). The
900
+ // SettingDefinition `type: 'object'` keeps the in-app dialog out of
901
+ // the way (we don't want a multi-line ASCII editor in the TUI), but
902
+ // the JSON Schema needs a real union so VS Code stops flagging the
903
+ // documented bare-string form.
904
+ // The `oneOf` here uses three *mutually exclusive* branches rather
905
+ // than one permissive object branch, so VS Code rejects nonsense
906
+ // like `{ path, small, large }` (which the runtime would also
907
+ // reject — see `normalizeTiers` in `customBanner.ts`).
908
+ jsonSchemaOverride: {
909
+ oneOf: [
910
+ { type: "string" },
911
+ // Bare `{path}` — no tier keys allowed.
912
+ {
913
+ type: "object",
914
+ properties: { path: { type: "string" } },
915
+ required: ["path"],
916
+ additionalProperties: false
917
+ },
918
+ // Width-aware `{small?, large?}` — `path` not allowed at this
919
+ // level; each tier is itself string-or-`{path}`.
920
+ {
921
+ type: "object",
922
+ properties: {
923
+ small: {
924
+ oneOf: [
925
+ { type: "string" },
926
+ {
927
+ type: "object",
928
+ properties: { path: { type: "string" } },
929
+ required: ["path"],
930
+ additionalProperties: false
931
+ }
932
+ ]
933
+ },
934
+ large: {
935
+ oneOf: [
936
+ { type: "string" },
937
+ {
938
+ type: "object",
939
+ properties: { path: { type: "string" } },
940
+ required: ["path"],
941
+ additionalProperties: false
942
+ }
943
+ ]
944
+ }
945
+ },
946
+ additionalProperties: false
947
+ }
948
+ ]
949
+ }
950
+ }
951
+ }
952
+ },
953
+ ide: {
954
+ type: "object",
955
+ label: "IDE",
956
+ category: "IDE",
957
+ requiresRestart: true,
958
+ default: {},
959
+ description: "IDE integration settings.",
960
+ showInDialog: false,
961
+ properties: {
962
+ enabled: {
963
+ type: "boolean",
964
+ label: "Auto-connect to IDE",
965
+ category: "IDE",
966
+ requiresRestart: true,
967
+ default: false,
968
+ description: "Enable IDE integration mode",
969
+ showInDialog: true
970
+ },
971
+ hasSeenNudge: {
972
+ type: "boolean",
973
+ label: "Has Seen IDE Integration Nudge",
974
+ category: "IDE",
975
+ requiresRestart: false,
976
+ default: false,
977
+ description: "Whether the user has seen the IDE integration nudge.",
978
+ showInDialog: false
979
+ }
980
+ }
981
+ },
982
+ privacy: {
983
+ type: "object",
984
+ label: "Privacy",
985
+ category: "Privacy",
986
+ requiresRestart: true,
987
+ default: {},
988
+ description: "Privacy-related settings.",
989
+ showInDialog: false,
990
+ properties: {
991
+ usageStatisticsEnabled: {
992
+ type: "boolean",
993
+ label: "Enable Usage Statistics",
994
+ category: "Privacy",
995
+ requiresRestart: true,
996
+ default: true,
997
+ description: "Enable collection of usage statistics",
998
+ showInDialog: true
999
+ }
1000
+ }
1001
+ },
1002
+ telemetry: {
1003
+ type: "object",
1004
+ label: "Telemetry",
1005
+ category: "Advanced",
1006
+ requiresRestart: true,
1007
+ default: void 0,
1008
+ description: "Telemetry configuration.",
1009
+ showInDialog: false,
1010
+ jsonSchemaOverride: {
1011
+ type: "object",
1012
+ properties: {
1013
+ includeSensitiveSpanAttributes: {
1014
+ description: "When enabled, user prompts, system prompts, tool inputs/outputs, and model responses are written to native OTel span attributes in addition to the log-to-span bridge. Warning: this may expose sensitive data (file contents, shell commands, conversation history) to your OTLP backend.",
1015
+ type: "boolean",
1016
+ default: false
1017
+ },
1018
+ sensitiveSpanAttributeMaxLength: {
1019
+ description: "Maximum JavaScript string length for each sensitive native OTel span attribute content payload. Default: 1048576 (1 MiB). Maximum: 104857600 (100 MiB). Set lower if your collector or backend rejects large span attributes.",
1020
+ type: "integer",
1021
+ minimum: 1,
1022
+ maximum: SENSITIVE_SPAN_ATTRIBUTE_MAX_LENGTH_LIMIT,
1023
+ default: DEFAULT_SENSITIVE_SPAN_ATTRIBUTE_MAX_LENGTH
1024
+ },
1025
+ resourceAttributes: {
1026
+ description: "Static resource attributes attached to every span/log/metric the SDK exports (OTLP or file outfile \u2014 they share the same Resource). Merged with the OTEL_RESOURCE_ATTRIBUTES env var; settings win on key conflict. Reserved keys (service.version, session.id) are dropped with a warning.",
1027
+ type: "object",
1028
+ additionalProperties: { type: "string" },
1029
+ default: {}
1030
+ },
1031
+ metrics: {
1032
+ description: "Per-signal cardinality controls for exported metrics.",
1033
+ type: "object",
1034
+ additionalProperties: false,
1035
+ properties: {
1036
+ includeSessionId: {
1037
+ description: "Include session.id on every metric data point. WARNING: each CLI session creates a new value, causing unbounded metric time-series fan-out at the backend. Only enable for short-term debugging \u2014 spans and logs still carry session.id.",
1038
+ type: "boolean",
1039
+ default: false
1040
+ }
1041
+ }
1042
+ }
1043
+ },
1044
+ additionalProperties: true
1045
+ }
1046
+ },
1047
+ outboundCorrelation: {
1048
+ type: "object",
1049
+ label: "Outbound Correlation",
1050
+ category: "Advanced",
1051
+ requiresRestart: true,
1052
+ default: void 0,
1053
+ description: "SECURITY-RELEVANT. Controls what client-side correlation data qwen-code writes into outbound LLM API requests (DashScope, OpenAI, Anthropic, etc.) \u2014 separate from `telemetry.*` which governs data flow into the operator's OWN OTLP collector. All values default to off. Opt in only when the LLM provider also reports into your OTel collector for cross-process trace stitching (e.g. ARMS Tracing + DashScope).",
1054
+ showInDialog: false,
1055
+ jsonSchemaOverride: {
1056
+ type: "object",
1057
+ properties: {
1058
+ propagateTraceContext: {
1059
+ description: "Requires `telemetry.enabled: true`. Inject W3C `traceparent` on outbound `fetch` requests (LLM SDK calls, MCP StreamableHTTP, WebFetch, ...) AND as a `TRACEPARENT` environment variable in shell child processes (Bash tool, hooks, monitor). When enabled, any existing `TRACEPARENT` in the parent environment is overwritten with qwen-code's own trace context. Default: false \u2014 trace context stays internal to the operator's OTLP collector. Set true when you want cross-process trace stitching with an OTel-aware LLM provider (e.g. ARMS+DashScope) or need shell scripts / CLI tools to participate in distributed tracing.",
1060
+ type: "boolean",
1061
+ default: false
1062
+ }
1063
+ },
1064
+ additionalProperties: false
1065
+ }
1066
+ },
1067
+ fastModel: {
1068
+ type: "string",
1069
+ label: "Fast Model",
1070
+ category: "Model",
1071
+ requiresRestart: false,
1072
+ default: "",
1073
+ description: "Model used for generating prompt suggestions and speculative execution. Leave empty to use the main model. A smaller/faster model (e.g., qwen3-coder-flash) reduces latency and cost.",
1074
+ showInDialog: true
1075
+ },
1076
+ visionModel: {
1077
+ type: "string",
1078
+ label: "Vision Model",
1079
+ category: "Model",
1080
+ requiresRestart: false,
1081
+ default: "",
1082
+ description: "Image-capable model used as the vision bridge: when a text-only main model receives an image, it is transcribed by this model first. Set with /model --vision. Leave empty to auto-pick a same-provider vision model.",
1083
+ showInDialog: true
1084
+ },
1085
+ voiceModel: {
1086
+ type: "string",
1087
+ label: "Voice Model",
1088
+ category: "Model",
1089
+ requiresRestart: false,
1090
+ default: "",
1091
+ description: "Model used for voice transcription. Set with /model --voice. Leave empty to keep voice dictation disabled until a voice model is selected.",
1092
+ showInDialog: false
1093
+ },
1094
+ model: {
1095
+ type: "object",
1096
+ label: "Model",
1097
+ category: "Model",
1098
+ requiresRestart: false,
1099
+ default: {},
1100
+ description: "Settings related to the generative model.",
1101
+ showInDialog: false,
1102
+ properties: {
1103
+ name: {
1104
+ type: "string",
1105
+ label: "Model",
1106
+ category: "Model",
1107
+ requiresRestart: false,
1108
+ default: void 0,
1109
+ description: "The model to use for conversations.",
1110
+ showInDialog: false
1111
+ },
1112
+ baseUrl: {
1113
+ type: "string",
1114
+ label: "Model Base URL",
1115
+ category: "Model",
1116
+ requiresRestart: false,
1117
+ default: void 0,
1118
+ description: "Base URL paired with model.name; disambiguates which provider to use when multiple modelProviders entries share the same model id.",
1119
+ showInDialog: false
1120
+ },
1121
+ maxSessionTurns: {
1122
+ type: "number",
1123
+ label: "Max Session Turns",
1124
+ category: "Model",
1125
+ requiresRestart: false,
1126
+ default: -1,
1127
+ description: "Maximum number of user/model/tool turns to keep in a session. -1 means unlimited.",
1128
+ showInDialog: false
1129
+ },
1130
+ maxWallTimeSeconds: {
1131
+ type: "number",
1132
+ label: "Max Wall-Clock Time (seconds)",
1133
+ category: "Model",
1134
+ requiresRestart: false,
1135
+ default: -1,
1136
+ description: "Run-level wall-clock budget for headless / unattended runs, in seconds. -1 means unlimited; otherwise must be in [1, ~2,147,483] (sub-second values and values above ~24 days are rejected as typos). Overridable per-invocation via --max-wall-time (which also accepts duration suffixes like 5m, 1.5h).",
1137
+ showInDialog: false
1138
+ },
1139
+ maxToolCalls: {
1140
+ type: "number",
1141
+ label: "Max Tool Calls",
1142
+ category: "Model",
1143
+ requiresRestart: false,
1144
+ default: -1,
1145
+ description: 'Cumulative tool-call budget for a run (counts every executed tool, success or failure; structured_output under --json-schema is exempt). -1 means unlimited; 0 means "no tool calls allowed" (first call aborts). Capped at 1,000,000 to catch typos. Overridable via --max-tool-calls.',
1146
+ showInDialog: false
1147
+ },
1148
+ chatCompression: {
1149
+ type: "object",
1150
+ label: "Chat Compression",
1151
+ category: "Model",
1152
+ requiresRestart: false,
1153
+ default: void 0,
1154
+ description: "Chat compression settings.",
1155
+ showInDialog: false
1156
+ },
1157
+ sessionTokenLimit: {
1158
+ type: "number",
1159
+ label: "Session Token Limit",
1160
+ category: "Model",
1161
+ requiresRestart: false,
1162
+ default: void 0,
1163
+ description: "The maximum number of tokens allowed in a session.",
1164
+ showInDialog: false
1165
+ },
1166
+ skipNextSpeakerCheck: {
1167
+ type: "boolean",
1168
+ label: "Skip Next Speaker Check",
1169
+ category: "Model",
1170
+ requiresRestart: false,
1171
+ default: true,
1172
+ description: "Skip the next speaker check.",
1173
+ showInDialog: false
1174
+ },
1175
+ skipWorkflowUsageWarning: {
1176
+ type: "boolean",
1177
+ label: "Skip Workflow Usage Warning",
1178
+ category: "Model",
1179
+ requiresRestart: false,
1180
+ default: false,
1181
+ description: "Suppress the one-time Workflow tool usage banner that describes the QWEN_CODE_MAX_TOKENS_PER_WORKFLOW env knob. The banner fires at most once per session regardless of this setting.",
1182
+ showInDialog: false
1183
+ },
1184
+ skipLoopDetection: {
1185
+ type: "boolean",
1186
+ label: "Skip Loop Detection",
1187
+ category: "Model",
1188
+ requiresRestart: false,
1189
+ default: true,
1190
+ description: "Skip the opt-in streaming loop-detection heuristics (content/thought repetition, read-file and action stagnation, global-duplicate and alternating tool-call patterns). Defaults to true to avoid false-positive interruptions; set to false to re-enable them as an unattended-run guardrail. A minimal always-on guard (consecutive identical tool calls plus a per-turn tool-call cap) still runs regardless of this setting.",
1191
+ showInDialog: false
1192
+ },
1193
+ skipStartupContext: {
1194
+ type: "boolean",
1195
+ label: "Skip Startup Context",
1196
+ category: "Model",
1197
+ requiresRestart: true,
1198
+ default: false,
1199
+ description: "Avoid sending the workspace startup context at the beginning of each session.",
1200
+ showInDialog: false
1201
+ },
1202
+ enableOpenAILogging: {
1203
+ type: "boolean",
1204
+ label: "Enable OpenAI Logging",
1205
+ category: "Model",
1206
+ requiresRestart: false,
1207
+ default: false,
1208
+ description: "Enable OpenAI logging.",
1209
+ showInDialog: false
1210
+ },
1211
+ openAILoggingDir: {
1212
+ type: "string",
1213
+ label: "OpenAI Logging Directory",
1214
+ category: "Model",
1215
+ requiresRestart: false,
1216
+ default: void 0,
1217
+ description: "Custom directory path for OpenAI API logs. If not specified, defaults to logs/openai in the current working directory.",
1218
+ showInDialog: false
1219
+ },
1220
+ generationConfig: {
1221
+ type: "object",
1222
+ label: "Generation Configuration",
1223
+ category: "Model",
1224
+ requiresRestart: false,
1225
+ default: void 0,
1226
+ description: "Generation configuration settings.",
1227
+ showInDialog: false,
1228
+ properties: {
1229
+ timeout: {
1230
+ type: "number",
1231
+ label: "Timeout",
1232
+ category: "Generation Configuration",
1233
+ requiresRestart: false,
1234
+ default: void 0,
1235
+ description: "Request timeout in milliseconds.",
1236
+ parentKey: "generationConfig",
1237
+ showInDialog: false
1238
+ },
1239
+ maxRetries: {
1240
+ type: "number",
1241
+ label: "Max Retries",
1242
+ category: "Generation Configuration",
1243
+ requiresRestart: false,
1244
+ default: void 0,
1245
+ description: "Maximum number of retries for failed requests.",
1246
+ parentKey: "generationConfig",
1247
+ showInDialog: false
1248
+ },
1249
+ enableCacheControl: {
1250
+ type: "boolean",
1251
+ label: "Enable Cache Control",
1252
+ category: "Generation Configuration",
1253
+ requiresRestart: false,
1254
+ default: true,
1255
+ description: "Enable cache control for DashScope providers.",
1256
+ parentKey: "generationConfig",
1257
+ showInDialog: false
1258
+ },
1259
+ splitToolMedia: {
1260
+ type: "boolean",
1261
+ label: "Split Tool Result Media",
1262
+ category: "Generation Configuration",
1263
+ requiresRestart: false,
1264
+ default: true,
1265
+ description: 'When true, media (images / audio / video / files) returned by tool calls \u2014 including the built-in read_file and MCP tools \u2014 is split into a follow-up user message instead of being embedded in the `role: "tool"` message. The OpenAI Chat Completions spec only permits text on tool messages, so strict OpenAI-compatible servers (e.g., doubao / new-api / LM Studio) silently drop or reject embedded media and the model never sees an image read via read_file (QwenLM/qwen-code#4876, #3616). Default true is spec-compliant and safe for permissive providers; set false only to restore the legacy embed-in-tool-message behavior.',
1266
+ parentKey: "generationConfig",
1267
+ showInDialog: false
1268
+ },
1269
+ toolResultContentFormat: {
1270
+ type: "enum",
1271
+ label: "Tool Result Content Format",
1272
+ category: "Generation Configuration",
1273
+ requiresRestart: false,
1274
+ default: "parts",
1275
+ description: 'Controls how text-only tool results are serialized in OpenAI-compatible requests. Use "parts" for the default content-part array shape. Use "string" only for legacy OpenAI-compatible runtimes whose tool templates ignore text content parts (for example older GLM-5.1 vLLM/SGLang templates; QwenLM/qwen-code#3361). Tool-returned media is still handled by splitToolMedia.',
1276
+ parentKey: "generationConfig",
1277
+ showInDialog: false,
1278
+ options: [
1279
+ { value: "parts", label: "Content Parts (Default)" },
1280
+ { value: "string", label: "String" }
1281
+ ]
1282
+ },
1283
+ schemaCompliance: {
1284
+ type: "enum",
1285
+ label: "Tool Schema Compliance",
1286
+ category: "Generation Configuration",
1287
+ requiresRestart: false,
1288
+ default: "auto",
1289
+ description: 'The compliance mode for tool schemas sent to the model. Use "openapi_30" for strict OpenAPI 3.0 compatibility (e.g., for Gemini).',
1290
+ parentKey: "generationConfig",
1291
+ showInDialog: false,
1292
+ options: [
1293
+ { value: "auto", label: "Auto (Default)" },
1294
+ { value: "openapi_30", label: "OpenAPI 3.0 Strict" }
1295
+ ]
1296
+ },
1297
+ contextWindowSize: {
1298
+ type: "number",
1299
+ label: "Context Window Size",
1300
+ category: "Generation Configuration",
1301
+ requiresRestart: false,
1302
+ default: void 0,
1303
+ description: "Overrides the default context window size for the selected model. Use this setting when a provider's effective context limit differs from Qwen Code's default. This value defines the model's assumed maximum context capacity, not a per-request token limit.",
1304
+ parentKey: "generationConfig",
1305
+ showInDialog: false
1306
+ }
1307
+ }
1308
+ }
1309
+ }
1310
+ },
1311
+ modelPricing: {
1312
+ type: "object",
1313
+ label: "Model Pricing",
1314
+ category: "Model",
1315
+ requiresRestart: false,
1316
+ default: void 0,
1317
+ description: 'Optional per-model pricing for cost estimation in /stats model. Example: {"qwen3-coder": {"inputPerMillionTokens": 0.30, "outputPerMillionTokens": 1.20}}',
1318
+ showInDialog: false
1319
+ },
1320
+ context: {
1321
+ type: "object",
1322
+ label: "Context",
1323
+ category: "Context",
1324
+ requiresRestart: false,
1325
+ default: {},
1326
+ description: "Settings for managing context provided to the model.",
1327
+ showInDialog: false,
1328
+ properties: {
1329
+ fileName: {
1330
+ type: "object",
1331
+ label: "Context File Name",
1332
+ category: "Context",
1333
+ requiresRestart: false,
1334
+ default: void 0,
1335
+ description: "The name of the context file or files.",
1336
+ showInDialog: false,
1337
+ jsonSchemaOverride: {
1338
+ anyOf: [
1339
+ { type: "string" },
1340
+ { type: "array", items: { type: "string" } }
1341
+ ]
1342
+ }
1343
+ },
1344
+ importFormat: {
1345
+ type: "enum",
1346
+ label: "Memory Import Format",
1347
+ category: "Context",
1348
+ requiresRestart: false,
1349
+ default: void 0,
1350
+ description: "The format to use when importing memory.",
1351
+ showInDialog: false,
1352
+ options: [
1353
+ { value: "tree", label: "Tree" },
1354
+ { value: "flat", label: "Flat" }
1355
+ ]
1356
+ },
1357
+ includeDirectories: {
1358
+ type: "array",
1359
+ label: "Include Directories",
1360
+ category: "Context",
1361
+ requiresRestart: false,
1362
+ default: [],
1363
+ description: "Additional directories to include in the workspace context. Missing directories will be skipped with a warning.",
1364
+ showInDialog: false,
1365
+ mergeStrategy: "concat" /* CONCAT */
1366
+ },
1367
+ loadFromIncludeDirectories: {
1368
+ type: "boolean",
1369
+ label: "Load Memory From Include Directories",
1370
+ category: "Context",
1371
+ requiresRestart: false,
1372
+ default: false,
1373
+ description: "Whether to load memory files from include directories.",
1374
+ showInDialog: false
1375
+ },
1376
+ clearContextOnIdle: {
1377
+ type: "object",
1378
+ label: "Clear Context On Idle",
1379
+ category: "Context",
1380
+ requiresRestart: false,
1381
+ default: {},
1382
+ description: "Settings for clearing stale or oversized tool result context. Use -1 to disable a threshold.",
1383
+ showInDialog: false,
1384
+ properties: {
1385
+ toolResultsThresholdMinutes: {
1386
+ type: "number",
1387
+ label: "Tool Results Idle Threshold (minutes)",
1388
+ category: "Context",
1389
+ requiresRestart: false,
1390
+ default: 60,
1391
+ description: "Minutes of inactivity before clearing old tool result content. Use -1 to disable.",
1392
+ showInDialog: false
1393
+ },
1394
+ toolResultsNumToKeep: {
1395
+ type: "number",
1396
+ label: "Tool Results Number To Keep",
1397
+ category: "Context",
1398
+ requiresRestart: false,
1399
+ default: 5,
1400
+ description: "Integer number of most-recent compactable tool results to preserve when clearing. Values below 1 are floored to 1.",
1401
+ jsonSchemaOverride: {
1402
+ type: "integer",
1403
+ default: 5,
1404
+ description: "Integer number of most-recent compactable tool results to preserve when clearing. Values below 1 are floored to 1."
1405
+ },
1406
+ showInDialog: false
1407
+ },
1408
+ toolResultsTotalCharsThreshold: {
1409
+ type: "number",
1410
+ label: "Tool Results Total Chars Threshold",
1411
+ category: "Context",
1412
+ requiresRestart: false,
1413
+ default: DEFAULT_TOOL_RESULTS_TOTAL_CHARS_THRESHOLD,
1414
+ description: "Total compactable tool result output characters allowed in history before clearing oldest results. Use -1 to disable. This is a soft threshold: protected recent tool results may keep the total above it.",
1415
+ showInDialog: false
1416
+ }
1417
+ }
1418
+ },
1419
+ fileFiltering: {
1420
+ type: "object",
1421
+ label: "File Filtering",
1422
+ category: "Context",
1423
+ requiresRestart: true,
1424
+ default: {},
1425
+ description: "Settings for git-aware file filtering.",
1426
+ showInDialog: false,
1427
+ properties: {
1428
+ respectGitIgnore: {
1429
+ type: "boolean",
1430
+ label: "Respect .gitignore",
1431
+ category: "Context",
1432
+ requiresRestart: true,
1433
+ default: true,
1434
+ description: "Respect .gitignore files when searching",
1435
+ showInDialog: true
1436
+ },
1437
+ respectQwenIgnore: {
1438
+ type: "boolean",
1439
+ label: "Respect .qwenignore",
1440
+ category: "Context",
1441
+ requiresRestart: true,
1442
+ default: true,
1443
+ description: "Respect .qwenignore and configured custom ignore files when searching",
1444
+ showInDialog: true
1445
+ },
1446
+ customIgnoreFiles: {
1447
+ type: "array",
1448
+ label: "Custom Ignore Files",
1449
+ category: "Context",
1450
+ requiresRestart: true,
1451
+ default: [...DEFAULT_QWEN_CUSTOM_IGNORE_FILE_NAMES],
1452
+ description: "Project-root-relative ignore files to use instead of the defaults (`.agentignore`, `.aiignore`) when respectQwenIgnore is enabled. .qwenignore is always included when respectQwenIgnore is enabled.",
1453
+ showInDialog: false,
1454
+ items: { type: "string" }
1455
+ },
1456
+ enableRecursiveFileSearch: {
1457
+ type: "boolean",
1458
+ label: "Enable Recursive File Search",
1459
+ category: "Context",
1460
+ requiresRestart: true,
1461
+ default: true,
1462
+ description: "Enable recursive file search functionality",
1463
+ showInDialog: false
1464
+ },
1465
+ enableFuzzySearch: {
1466
+ type: "boolean",
1467
+ label: "Enable Fuzzy Search",
1468
+ category: "Context",
1469
+ requiresRestart: true,
1470
+ default: true,
1471
+ description: "Enable fuzzy search when searching for files.",
1472
+ showInDialog: true
1473
+ }
1474
+ }
1475
+ },
1476
+ autoCompactThreshold: {
1477
+ type: "number",
1478
+ label: "Auto-Compact Threshold",
1479
+ category: "Context",
1480
+ requiresRestart: false,
1481
+ default: void 0,
1482
+ description: "Fraction of context window at which auto-compaction triggers (greater than 0, up to 1). Default is 0.7 (70%).",
1483
+ showInDialog: false,
1484
+ jsonSchemaOverride: {
1485
+ type: "number",
1486
+ minimum: 0.01,
1487
+ maximum: 1
1488
+ }
1489
+ }
1490
+ }
1491
+ },
1492
+ memory: {
1493
+ type: "object",
1494
+ label: "Memory",
1495
+ category: "Memory",
1496
+ requiresRestart: false,
1497
+ default: {},
1498
+ description: "Settings for managed auto-memory.",
1499
+ showInDialog: false,
1500
+ properties: {
1501
+ enableManagedAutoMemory: {
1502
+ type: "boolean",
1503
+ label: "Enable Managed Auto-Memory",
1504
+ category: "Memory",
1505
+ requiresRestart: false,
1506
+ default: true,
1507
+ description: "Enable background extraction of memories from conversations.",
1508
+ showInDialog: false
1509
+ },
1510
+ enableManagedAutoDream: {
1511
+ type: "boolean",
1512
+ label: "Enable Managed Auto-Dream",
1513
+ category: "Memory",
1514
+ requiresRestart: false,
1515
+ default: true,
1516
+ description: "Enable automatic consolidation (dream) of collected memories.",
1517
+ showInDialog: false
1518
+ },
1519
+ enableAutoSkill: {
1520
+ type: "boolean",
1521
+ label: "Enable Auto Skill",
1522
+ category: "Memory",
1523
+ requiresRestart: false,
1524
+ default: true,
1525
+ description: "Enable background review for reusable project skills after tool-heavy sessions.",
1526
+ showInDialog: false
1527
+ },
1528
+ autoSkillConfirm: {
1529
+ type: "boolean",
1530
+ label: "Confirm Auto Skills Before Saving",
1531
+ category: "Memory",
1532
+ requiresRestart: false,
1533
+ default: true,
1534
+ description: "Ask for confirmation before auto-generated skills are added to the skill library. When off, auto-skills are saved immediately.",
1535
+ showInDialog: false
1536
+ },
1537
+ enableTeamMemory: {
1538
+ type: "boolean",
1539
+ label: "Enable Team Memory",
1540
+ category: "Memory",
1541
+ requiresRestart: false,
1542
+ default: false,
1543
+ description: "Enable a project memory tier shared with collaborators via the git-tracked `.qwen/team-memory/` directory. Off by default; writes to it are secret-scanned and reviewable in the git diff.",
1544
+ showInDialog: false
1545
+ },
1546
+ enableTeamMemorySync: {
1547
+ type: "boolean",
1548
+ label: "Enable Team Memory Git Sync",
1549
+ category: "Memory",
1550
+ requiresRestart: false,
1551
+ default: false,
1552
+ description: "When team memory is enabled, automatically commit, fast-forward-pull, and push the `.qwen/team-memory/` directory at session start so collaborators stay in sync. Off by default; requires a configured git upstream.",
1553
+ showInDialog: false
1554
+ }
1555
+ }
1556
+ },
1557
+ slashCommands: {
1558
+ type: "object",
1559
+ label: "Slash Commands",
1560
+ category: "Advanced",
1561
+ requiresRestart: true,
1562
+ default: {},
1563
+ description: "Configuration for slash commands exposed by the CLI. Useful for locking down the command surface in multi-tenant or enterprise deployments.",
1564
+ showInDialog: false,
1565
+ properties: {
1566
+ disabled: {
1567
+ type: "array",
1568
+ label: "Disabled Slash Commands",
1569
+ category: "Advanced",
1570
+ requiresRestart: true,
1571
+ default: void 0,
1572
+ description: 'Slash command names to hide and refuse to execute. Matched case-insensitively against the final command name (for extension commands this is the disambiguated form, e.g. "myext.deploy"). Merged as a union across settings scopes, so workspace settings can add to but not remove entries defined in system/user settings.',
1573
+ showInDialog: false,
1574
+ mergeStrategy: "union" /* UNION */
1575
+ }
1576
+ }
1577
+ },
1578
+ skills: {
1579
+ type: "object",
1580
+ label: "Skills",
1581
+ category: "Advanced",
1582
+ requiresRestart: false,
1583
+ default: {},
1584
+ description: "Configuration for skills (SKILL.md-based capabilities) exposed to the model.",
1585
+ showInDialog: false,
1586
+ properties: {
1587
+ disabled: {
1588
+ type: "array",
1589
+ label: "Disabled Skills",
1590
+ category: "Advanced",
1591
+ requiresRestart: false,
1592
+ default: void 0,
1593
+ description: "Skill names to hide. Matched case-insensitively against the skill name. Hidden skills do not appear in <available_skills> or as /<name> slash commands. UNION-merged across systemDefaults/user/workspace/system scopes \u2014 workspace cannot remove entries defined in higher scopes.",
1594
+ showInDialog: false,
1595
+ mergeStrategy: "union" /* UNION */
1596
+ }
1597
+ }
1598
+ },
1599
+ permissions: {
1600
+ type: "object",
1601
+ label: "Permissions",
1602
+ category: "Tools",
1603
+ requiresRestart: true,
1604
+ default: {},
1605
+ description: "Permission rules controlling tool usage. Rules are evaluated in priority order: deny > ask > allow.",
1606
+ showInDialog: false,
1607
+ properties: {
1608
+ allow: {
1609
+ type: "array",
1610
+ label: "Allow Rules",
1611
+ category: "Tools",
1612
+ requiresRestart: true,
1613
+ default: void 0,
1614
+ description: 'Tools or commands that are auto-approved without confirmation. Examples: "ShellTool", "Bash(git *)", "ReadFileTool".',
1615
+ showInDialog: false,
1616
+ mergeStrategy: "union" /* UNION */
1617
+ },
1618
+ ask: {
1619
+ type: "array",
1620
+ label: "Ask Rules",
1621
+ category: "Tools",
1622
+ requiresRestart: true,
1623
+ default: void 0,
1624
+ description: "Tools or commands that always require user confirmation. Takes precedence over allow rules.",
1625
+ showInDialog: false,
1626
+ mergeStrategy: "union" /* UNION */
1627
+ },
1628
+ deny: {
1629
+ type: "array",
1630
+ label: "Deny Rules",
1631
+ category: "Tools",
1632
+ requiresRestart: true,
1633
+ default: void 0,
1634
+ description: 'Tools or commands that are always blocked. Highest priority rule. Examples: "ShellTool", "Bash(rm -rf *)".',
1635
+ showInDialog: false,
1636
+ mergeStrategy: "union" /* UNION */
1637
+ },
1638
+ autoMode: {
1639
+ type: "object",
1640
+ label: "Auto Mode",
1641
+ category: "Tools",
1642
+ requiresRestart: true,
1643
+ default: {},
1644
+ description: "Settings consumed by the AUTO approval mode classifier.",
1645
+ showInDialog: false,
1646
+ properties: {
1647
+ classifier: {
1648
+ type: "object",
1649
+ label: "Auto Mode Classifier",
1650
+ category: "Tools",
1651
+ requiresRestart: true,
1652
+ default: {},
1653
+ description: "Runtime controls for the AUTO approval mode classifier.",
1654
+ showInDialog: false,
1655
+ properties: {
1656
+ timeouts: {
1657
+ type: "object",
1658
+ label: "Auto Mode Classifier Timeouts",
1659
+ category: "Tools",
1660
+ requiresRestart: true,
1661
+ default: {},
1662
+ description: "Timeouts for the two AUTO classifier stages, in milliseconds.",
1663
+ showInDialog: false,
1664
+ properties: {
1665
+ stage1Ms: {
1666
+ type: "number",
1667
+ label: "Auto Mode Stage 1 Timeout",
1668
+ category: "Tools",
1669
+ requiresRestart: true,
1670
+ default: void 0,
1671
+ description: "Timeout in milliseconds for the fast stage-1 AUTO classifier.",
1672
+ showInDialog: false
1673
+ },
1674
+ stage2Ms: {
1675
+ type: "number",
1676
+ label: "Auto Mode Stage 2 Timeout",
1677
+ category: "Tools",
1678
+ requiresRestart: true,
1679
+ default: void 0,
1680
+ description: "Timeout in milliseconds for the stage-2 AUTO classifier review.",
1681
+ showInDialog: false
1682
+ }
1683
+ }
1684
+ },
1685
+ thinking: {
1686
+ type: "object",
1687
+ label: "Auto Mode Classifier Thinking",
1688
+ category: "Tools",
1689
+ requiresRestart: true,
1690
+ default: {},
1691
+ description: "Provider/API-level thinking controls for the AUTO classifier.",
1692
+ showInDialog: false,
1693
+ properties: {
1694
+ stage2Enabled: {
1695
+ type: "boolean",
1696
+ label: "Auto Mode Stage 2 Thinking",
1697
+ category: "Tools",
1698
+ requiresRestart: true,
1699
+ default: false,
1700
+ description: "Whether stage 2 may use provider/API-level thinking. Stage 1 always keeps thinking disabled.",
1701
+ showInDialog: false
1702
+ }
1703
+ }
1704
+ }
1705
+ }
1706
+ },
1707
+ hints: {
1708
+ type: "object",
1709
+ label: "Classifier Hints",
1710
+ category: "Tools",
1711
+ requiresRestart: true,
1712
+ default: {},
1713
+ description: "Natural-language hints injected into the classifier system prompt.",
1714
+ showInDialog: false,
1715
+ properties: {
1716
+ allow: {
1717
+ type: "array",
1718
+ label: "Auto Mode Allow Hints",
1719
+ category: "Tools",
1720
+ requiresRestart: true,
1721
+ default: void 0,
1722
+ description: "Natural-language descriptions of actions AUTO mode should allow.",
1723
+ showInDialog: false,
1724
+ mergeStrategy: "union" /* UNION */
1725
+ },
1726
+ softDeny: {
1727
+ type: "array",
1728
+ label: "Auto Mode Soft-Deny Hints",
1729
+ category: "Tools",
1730
+ requiresRestart: true,
1731
+ default: void 0,
1732
+ description: "Natural-language descriptions of destructive / irreversible actions AUTO mode should block unless the user explicitly authorised that exact action and scope.",
1733
+ showInDialog: false,
1734
+ mergeStrategy: "union" /* UNION */
1735
+ },
1736
+ hardDeny: {
1737
+ type: "array",
1738
+ label: "Auto Mode Hard-Deny Hints",
1739
+ category: "Tools",
1740
+ requiresRestart: true,
1741
+ default: void 0,
1742
+ description: "Natural-language descriptions of security-boundary actions the AUTO classifier must block even when an autoMode allow hint or recent user request would normally authorise them. Does not override permissions.allow; use permissions.deny for deterministic hard permission rules.",
1743
+ showInDialog: false,
1744
+ mergeStrategy: "union" /* UNION */
1745
+ },
1746
+ deny: {
1747
+ type: "array",
1748
+ label: "Auto Mode Deny Hints (legacy)",
1749
+ category: "Tools",
1750
+ requiresRestart: true,
1751
+ default: void 0,
1752
+ description: "Deprecated alias for `softDeny`. Entries here are merged into the SOFT BLOCK user section so existing settings keep working; new configurations should use `softDeny` or `hardDeny` instead.",
1753
+ showInDialog: false,
1754
+ mergeStrategy: "union" /* UNION */
1755
+ }
1756
+ }
1757
+ },
1758
+ environment: {
1759
+ type: "array",
1760
+ label: "Auto Mode Environment",
1761
+ category: "Tools",
1762
+ requiresRestart: true,
1763
+ default: void 0,
1764
+ description: "Environment / context lines injected into the classifier system prompt.",
1765
+ showInDialog: false,
1766
+ mergeStrategy: "union" /* UNION */
1767
+ }
1768
+ }
1769
+ }
1770
+ }
1771
+ },
1772
+ tools: {
1773
+ type: "object",
1774
+ label: "Tools",
1775
+ category: "Tools",
1776
+ requiresRestart: true,
1777
+ default: {},
1778
+ description: "Settings for built-in and custom tools.",
1779
+ showInDialog: false,
1780
+ properties: {
1781
+ sandbox: {
1782
+ type: "object",
1783
+ label: "Sandbox",
1784
+ category: "Tools",
1785
+ requiresRestart: true,
1786
+ default: void 0,
1787
+ description: "Sandbox execution environment (can be a boolean or a path string).",
1788
+ showInDialog: false,
1789
+ jsonSchemaOverride: {
1790
+ anyOf: [{ type: "boolean" }, { type: "string" }]
1791
+ }
1792
+ },
1793
+ sandboxImage: {
1794
+ type: "string",
1795
+ label: "Sandbox Image",
1796
+ category: "Tools",
1797
+ requiresRestart: true,
1798
+ default: void 0,
1799
+ description: "Sandbox image URI used by Docker/Podman when --sandbox-image and QWEN_SANDBOX_IMAGE are not set.",
1800
+ showInDialog: false
1801
+ },
1802
+ toolSearch: {
1803
+ type: "object",
1804
+ label: "Tool Search",
1805
+ category: "Tools",
1806
+ requiresRestart: true,
1807
+ default: {},
1808
+ description: "Settings for the ToolSearch discovery mechanism.",
1809
+ showInDialog: false,
1810
+ properties: {
1811
+ enabled: {
1812
+ type: "boolean",
1813
+ label: "Enable ToolSearch",
1814
+ category: "Tools",
1815
+ requiresRestart: true,
1816
+ default: true,
1817
+ description: "When enabled, MCP tools are loaded on-demand via ToolSearch to reduce prompt size. Disable this for models that rely on prefix-based KV caching (e.g. DeepSeek) to keep the prompt prefix stable and maximize cache hit rates.",
1818
+ showInDialog: true
1819
+ }
1820
+ }
1821
+ },
1822
+ shell: {
1823
+ type: "object",
1824
+ label: "Shell",
1825
+ category: "Tools",
1826
+ requiresRestart: false,
1827
+ default: {},
1828
+ description: "Settings for shell execution.",
1829
+ showInDialog: false,
1830
+ properties: {
1831
+ enableInteractiveShell: {
1832
+ type: "boolean",
1833
+ label: "Interactive Shell (PTY)",
1834
+ category: "Tools",
1835
+ requiresRestart: true,
1836
+ default: true,
1837
+ description: "Use node-pty for an interactive shell experience. Falls back to child_process if PTY is unavailable.",
1838
+ showInDialog: true
1839
+ },
1840
+ pager: {
1841
+ type: "string",
1842
+ label: "Pager",
1843
+ category: "Tools",
1844
+ requiresRestart: false,
1845
+ default: "cat",
1846
+ description: "The pager command to use for shell output. Defaults to `cat`.",
1847
+ showInDialog: false
1848
+ },
1849
+ showColor: {
1850
+ type: "boolean",
1851
+ label: "Show Color",
1852
+ category: "Tools",
1853
+ requiresRestart: false,
1854
+ default: false,
1855
+ description: "Show color in shell output.",
1856
+ showInDialog: false
1857
+ }
1858
+ }
1859
+ },
1860
+ // Legacy tool permission fields – kept for backward compatibility.
1861
+ // Use permissions.{allow,ask,deny} instead.
1862
+ core: {
1863
+ type: "array",
1864
+ label: "Core Tools (deprecated)",
1865
+ category: "Tools",
1866
+ requiresRestart: true,
1867
+ default: void 0,
1868
+ description: "Deprecated. Use permissions.allow instead.",
1869
+ showInDialog: false
1870
+ },
1871
+ allowed: {
1872
+ type: "array",
1873
+ label: "Allowed Tools (deprecated)",
1874
+ category: "Advanced",
1875
+ requiresRestart: true,
1876
+ default: void 0,
1877
+ description: "Deprecated. Use permissions.allow instead.",
1878
+ showInDialog: false
1879
+ },
1880
+ exclude: {
1881
+ type: "array",
1882
+ label: "Exclude Tools (deprecated)",
1883
+ category: "Tools",
1884
+ requiresRestart: true,
1885
+ default: void 0,
1886
+ description: "Deprecated. Use permissions.deny instead.",
1887
+ showInDialog: false,
1888
+ mergeStrategy: "union" /* UNION */
1889
+ },
1890
+ disabled: {
1891
+ type: "array",
1892
+ label: "Disabled Tools",
1893
+ category: "Tools",
1894
+ requiresRestart: true,
1895
+ default: void 0,
1896
+ description: "Tool names hidden from the registry. Differs from permissions.deny: disabled tools are not registered at all, so they never appear in /tools and cannot be discovered by the model. Managed by the daemon mutation route POST /workspace/tools/:name/enable.",
1897
+ showInDialog: false,
1898
+ mergeStrategy: "union" /* UNION */
1899
+ },
1900
+ approvalMode: {
1901
+ type: "enum",
1902
+ label: "Tool Approval Mode",
1903
+ category: "Tools",
1904
+ requiresRestart: false,
1905
+ default: "default" /* DEFAULT */,
1906
+ description: "Approval mode for tool usage. Controls how tools are approved before execution.",
1907
+ showInDialog: true,
1908
+ options: [
1909
+ { value: "plan" /* PLAN */, label: "Plan" },
1910
+ { value: "default" /* DEFAULT */, label: "Ask permissions" },
1911
+ { value: "auto-edit" /* AUTO_EDIT */, label: "Auto Edit" },
1912
+ { value: "auto" /* AUTO */, label: "Auto" },
1913
+ { value: "yolo" /* YOLO */, label: "YOLO" }
1914
+ ]
1915
+ },
1916
+ autoAccept: {
1917
+ type: "boolean",
1918
+ label: "Auto Accept",
1919
+ category: "Tools",
1920
+ requiresRestart: false,
1921
+ default: false,
1922
+ description: "Automatically accept and execute tool calls that are considered safe (e.g., read-only operations) without explicit user confirmation.",
1923
+ showInDialog: false
1924
+ },
1925
+ discoveryCommand: {
1926
+ type: "string",
1927
+ label: "Tool Discovery Command",
1928
+ category: "Tools",
1929
+ requiresRestart: true,
1930
+ default: void 0,
1931
+ description: "Command to run for tool discovery.",
1932
+ showInDialog: false
1933
+ },
1934
+ callCommand: {
1935
+ type: "string",
1936
+ label: "Tool Call Command",
1937
+ category: "Tools",
1938
+ requiresRestart: true,
1939
+ default: void 0,
1940
+ description: "Command to run for tool calls.",
1941
+ showInDialog: false
1942
+ },
1943
+ useRipgrep: {
1944
+ type: "boolean",
1945
+ label: "Use Ripgrep",
1946
+ category: "Tools",
1947
+ requiresRestart: false,
1948
+ default: true,
1949
+ description: "Use ripgrep for file content search instead of the fallback implementation. Provides faster search performance.",
1950
+ showInDialog: false
1951
+ },
1952
+ useBuiltinRipgrep: {
1953
+ type: "boolean",
1954
+ label: "Use Builtin Ripgrep",
1955
+ category: "Tools",
1956
+ requiresRestart: false,
1957
+ default: true,
1958
+ description: 'Use the bundled ripgrep binary. When set to false, the system-level "rg" command will be used instead. This setting is only effective when useRipgrep is true.',
1959
+ showInDialog: false
1960
+ },
1961
+ truncateToolOutputThreshold: {
1962
+ type: "number",
1963
+ label: "Tool Output Truncation Threshold",
1964
+ category: "General",
1965
+ requiresRestart: true,
1966
+ default: DEFAULT_TRUNCATE_TOOL_OUTPUT_THRESHOLD,
1967
+ description: "Truncate tool output if it is larger than this many characters. Set to -1 to disable.",
1968
+ showInDialog: false
1969
+ },
1970
+ truncateToolOutputLines: {
1971
+ type: "number",
1972
+ label: "Tool Output Truncation Lines",
1973
+ category: "General",
1974
+ requiresRestart: true,
1975
+ default: DEFAULT_TRUNCATE_TOOL_OUTPUT_LINES,
1976
+ description: "The number of lines to keep when truncating tool output.",
1977
+ showInDialog: false
1978
+ },
1979
+ toolOutputBatchBudget: {
1980
+ type: "number",
1981
+ label: "Tool Output Batch Budget",
1982
+ category: "General",
1983
+ requiresRestart: true,
1984
+ default: DEFAULT_TOOL_OUTPUT_BATCH_BUDGET,
1985
+ description: "Per-message budget (characters) for the combined output of one batch of tool calls; the largest results are offloaded to disk when exceeded. Set to -1 to disable.",
1986
+ showInDialog: false
1987
+ },
1988
+ computerUse: {
1989
+ type: "object",
1990
+ label: "Computer Use",
1991
+ category: "Tools",
1992
+ requiresRestart: true,
1993
+ default: {},
1994
+ description: "Cross-platform desktop automation via the cua-driver native driver (trycua/cua). On first invocation a pinned, signed + notarized binary (~20MB) is downloaded into ~/.qwen/computer-use/ and the user is walked through macOS Accessibility / Screen Recording permissions if needed. Exposes cua-driver's full tool surface (click, type_text, scroll, drag, press_key, get_window_state, page, launch_app, and more).",
1995
+ showInDialog: false,
1996
+ properties: {
1997
+ enabled: {
1998
+ type: "boolean",
1999
+ label: "Enable Computer Use",
2000
+ category: "Tools",
2001
+ requiresRestart: true,
2002
+ default: true,
2003
+ description: "When enabled (default), the cua-driver computer_use__* tools are registered as deferred built-ins. Set to false to prevent the driver from being downloaded or spawned.",
2004
+ showInDialog: true
2005
+ },
2006
+ idleTimeoutMs: {
2007
+ type: "number",
2008
+ label: "Idle Timeout",
2009
+ category: "Tools",
2010
+ requiresRestart: true,
2011
+ default: 3e5,
2012
+ minimum: 0,
2013
+ maximum: 2147483647,
2014
+ description: "Milliseconds to keep the cua-driver process alive after the last computer_use__* call. The default is 300000 (5 minutes). Set to 0 to keep it running until qwen-code exits.",
2015
+ showInDialog: false
2016
+ },
2017
+ maxImageDimension: {
2018
+ type: "number",
2019
+ label: "Max Screenshot Dimension",
2020
+ category: "Tools",
2021
+ requiresRestart: true,
2022
+ default: -1,
2023
+ description: "Longest-edge pixel cap applied to cua-driver screenshots (via set_config's max_image_dimension). -1 (default) keeps cua-driver's built-in default (1568); 0 disables resizing (full resolution); a positive value caps the longest edge. Lower caps cut vision-token cost at the expense of fine detail. Overridable via the QWEN_COMPUTER_USE_MAX_IMAGE_DIMENSION env var.",
2024
+ showInDialog: false
2025
+ }
2026
+ }
2027
+ }
2028
+ }
2029
+ },
2030
+ policy: {
2031
+ type: "object",
2032
+ label: "Daemon Policy",
2033
+ category: "Daemon",
2034
+ requiresRestart: true,
2035
+ default: {},
2036
+ description: "Daemon multi-client coordination policies. Tool-level allow/deny rules live under `permissions`; this section is for runtime mediation behavior between concurrent HTTP clients sharing one `qwen serve` daemon.",
2037
+ showInDialog: false,
2038
+ properties: {
2039
+ permissionStrategy: {
2040
+ type: "enum",
2041
+ label: "Permission Mediation Policy",
2042
+ category: "Daemon",
2043
+ requiresRestart: true,
2044
+ default: "first-responder",
2045
+ description: 'How permission requests resolve when multiple clients are attached. `first-responder` (default) = any client decides, first wins. `designated` = only the prompt originator decides; falls back to first-responder if originator is anonymous. NOTE: client identity comes from self-declared X-Qwen-Client-Id with no proof-of-possession (pair-token identity is not implemented yet), so any client observing originatorClientId on SSE frames can register with the same id and impersonate the originator. `consensus` = N-of-M voters must agree. Default N=floor(M/2)+1, which means UNANIMITY for M=2 (quorum=2, both must agree) and supermajority for larger even M (M=4 \u2192 quorum=3; M=6 \u2192 quorum=4). For M=2 specifically, split votes resolve only via permissionTimeoutMs. `local-only` = only loopback clients can RESOLVE; remote clients can still ABORT a pending permission via the cancel sentinel ({outcome:"cancelled"}) \u2014 cancel stays cross-policy for consistency. Strict-cancel-too deployments need a dedicated loopback-bound daemon. Requires daemon restart \u2014 read once at boot.',
2046
+ showInDialog: true,
2047
+ options: [
2048
+ { value: "first-responder", label: "First Responder" },
2049
+ { value: "designated", label: "Designated Originator" },
2050
+ { value: "consensus", label: "Consensus Quorum" },
2051
+ { value: "local-only", label: "Local Only" }
2052
+ ]
2053
+ },
2054
+ consensusQuorum: {
2055
+ type: "number",
2056
+ label: "Consensus Quorum Override",
2057
+ category: "Daemon",
2058
+ requiresRestart: true,
2059
+ default: void 0,
2060
+ description: "Optional fixed quorum size for consensus policy. Capped at M (count of registered voters at request issue time) to prevent unreachable quorum. Unset = floor(M/2)+1. Requires daemon restart \u2014 read once at boot.",
2061
+ showInDialog: false,
2062
+ // run-qwen-serve.ts validates `Number.isInteger(n) && n >= 1` and
2063
+ // refuses to boot otherwise. Override the generated schema so IDE
2064
+ // (VSCode, JetBrains via JSON Schema) flags `0`, `-1`, `1.5`
2065
+ // BEFORE the user restarts the daemon. The bare `type:'number'`
2066
+ // mapping accepts all of these.
2067
+ jsonSchemaOverride: {
2068
+ type: "integer",
2069
+ minimum: 1,
2070
+ description: "Optional fixed quorum size for consensus policy. Capped at M (count of registered voters at request issue time) to prevent unreachable quorum. Unset = floor(M/2)+1. Requires daemon restart \u2014 read once at boot."
2071
+ }
2072
+ }
2073
+ }
2074
+ },
2075
+ mcp: {
2076
+ type: "object",
2077
+ label: "MCP",
2078
+ category: "MCP",
2079
+ requiresRestart: true,
2080
+ default: {},
2081
+ description: "Settings for Model Context Protocol (MCP) servers.",
2082
+ showInDialog: false,
2083
+ properties: {
2084
+ serverCommand: {
2085
+ type: "string",
2086
+ label: "MCP Server Command",
2087
+ category: "MCP",
2088
+ requiresRestart: true,
2089
+ default: void 0,
2090
+ description: "Command to start an MCP server.",
2091
+ showInDialog: false
2092
+ },
2093
+ allowed: {
2094
+ type: "array",
2095
+ label: "Allow MCP Servers",
2096
+ category: "MCP",
2097
+ // Hot-reloadable (sub-task 3): read by mcpGatingEqual and re-applied
2098
+ // live during reconcile. See mcpServers above for why this must not be
2099
+ // restart-required.
2100
+ requiresRestart: false,
2101
+ default: void 0,
2102
+ description: "A list of MCP servers to allow.",
2103
+ showInDialog: false,
2104
+ mergeStrategy: "concat" /* CONCAT */
2105
+ },
2106
+ excluded: {
2107
+ type: "array",
2108
+ label: "Exclude MCP Servers",
2109
+ category: "MCP",
2110
+ // Hot-reloadable (sub-task 3): read by mcpGatingEqual and re-applied
2111
+ // live during reconcile. See mcpServers above for why this must not be
2112
+ // restart-required.
2113
+ requiresRestart: false,
2114
+ default: void 0,
2115
+ description: "A list of MCP servers to exclude.",
2116
+ showInDialog: false,
2117
+ mergeStrategy: "concat" /* CONCAT */
2118
+ }
2119
+ }
2120
+ },
2121
+ security: {
2122
+ type: "object",
2123
+ label: "Security",
2124
+ category: "Security",
2125
+ requiresRestart: true,
2126
+ default: {},
2127
+ description: "Security-related settings.",
2128
+ showInDialog: false,
2129
+ properties: {
2130
+ folderTrust: {
2131
+ type: "object",
2132
+ label: "Folder Trust",
2133
+ category: "Security",
2134
+ requiresRestart: false,
2135
+ default: {},
2136
+ description: "Settings for folder trust.",
2137
+ showInDialog: false,
2138
+ properties: {
2139
+ enabled: {
2140
+ type: "boolean",
2141
+ label: "Folder Trust",
2142
+ category: "Security",
2143
+ requiresRestart: true,
2144
+ default: false,
2145
+ description: "Setting to track whether Folder trust is enabled.",
2146
+ showInDialog: false
2147
+ }
2148
+ }
2149
+ },
2150
+ auth: {
2151
+ type: "object",
2152
+ label: "Authentication",
2153
+ category: "Security",
2154
+ requiresRestart: true,
2155
+ default: {},
2156
+ description: "Authentication settings.",
2157
+ showInDialog: false,
2158
+ properties: {
2159
+ selectedType: {
2160
+ type: "string",
2161
+ label: "Selected Auth Type",
2162
+ category: "Security",
2163
+ requiresRestart: true,
2164
+ default: void 0,
2165
+ description: "The currently selected authentication type.",
2166
+ showInDialog: false
2167
+ },
2168
+ enforcedType: {
2169
+ type: "string",
2170
+ label: "Enforced Auth Type",
2171
+ category: "Advanced",
2172
+ requiresRestart: true,
2173
+ default: void 0,
2174
+ description: "The required auth type. If this does not match the selected auth type, the user will be prompted to re-authenticate.",
2175
+ showInDialog: false
2176
+ },
2177
+ useExternal: {
2178
+ type: "boolean",
2179
+ label: "Use External Auth",
2180
+ category: "Security",
2181
+ requiresRestart: true,
2182
+ default: void 0,
2183
+ description: "Whether to use an external authentication flow.",
2184
+ showInDialog: false
2185
+ },
2186
+ apiKey: {
2187
+ type: "string",
2188
+ label: "API Key",
2189
+ category: "Security",
2190
+ requiresRestart: true,
2191
+ default: void 0,
2192
+ description: "API key for OpenAI compatible authentication.",
2193
+ showInDialog: false
2194
+ },
2195
+ baseUrl: {
2196
+ type: "string",
2197
+ label: "Base URL",
2198
+ category: "Security",
2199
+ requiresRestart: true,
2200
+ default: void 0,
2201
+ description: "Base URL for OpenAI compatible API.",
2202
+ showInDialog: false
2203
+ }
2204
+ }
2205
+ },
2206
+ allowedHttpHookUrls: {
2207
+ type: "array",
2208
+ label: "Allowed HTTP Hook URLs",
2209
+ category: "Security",
2210
+ requiresRestart: false,
2211
+ default: [],
2212
+ description: "Whitelist of URL patterns for HTTP hooks. Supports * wildcard. If empty, all URLs are allowed (subject to SSRF protection).",
2213
+ showInDialog: false,
2214
+ items: {
2215
+ type: "string",
2216
+ description: "URL pattern (supports * wildcard)"
2217
+ }
2218
+ }
2219
+ }
2220
+ },
2221
+ advanced: {
2222
+ type: "object",
2223
+ label: "Advanced",
2224
+ category: "Advanced",
2225
+ requiresRestart: true,
2226
+ default: {},
2227
+ description: "Advanced settings for power users.",
2228
+ showInDialog: false,
2229
+ properties: {
2230
+ autoConfigureMemory: {
2231
+ type: "boolean",
2232
+ label: "Auto Configure Max Old Space Size",
2233
+ category: "Advanced",
2234
+ requiresRestart: true,
2235
+ default: false,
2236
+ description: "Automatically configure Node.js memory limits",
2237
+ showInDialog: false
2238
+ },
2239
+ dnsResolutionOrder: {
2240
+ type: "enum",
2241
+ label: "DNS Resolution Order",
2242
+ category: "Advanced",
2243
+ requiresRestart: true,
2244
+ default: void 0,
2245
+ description: "The DNS resolution order.",
2246
+ showInDialog: false,
2247
+ options: [
2248
+ { value: "ipv4first", label: "IPv4 First" },
2249
+ { value: "verbatim", label: "Verbatim" }
2250
+ ]
2251
+ },
2252
+ excludedEnvVars: {
2253
+ type: "array",
2254
+ label: "Excluded Project Environment Variables",
2255
+ category: "Advanced",
2256
+ requiresRestart: false,
2257
+ default: ["DEBUG", "DEBUG_MODE"],
2258
+ description: "Environment variables to exclude from project context.",
2259
+ showInDialog: false,
2260
+ mergeStrategy: "union" /* UNION */
2261
+ },
2262
+ bugCommand: {
2263
+ type: "object",
2264
+ label: "Bug Command",
2265
+ category: "Advanced",
2266
+ requiresRestart: false,
2267
+ default: void 0,
2268
+ description: "Configuration for the bug report command.",
2269
+ showInDialog: false
2270
+ },
2271
+ runtimeOutputDir: {
2272
+ type: "string",
2273
+ label: "Runtime Output Directory",
2274
+ category: "Advanced",
2275
+ requiresRestart: true,
2276
+ default: void 0,
2277
+ description: "Custom directory for runtime output (temp files, debug logs, session data, todos, etc.). Config files remain at ~/.qwen (or QWEN_HOME if set). Env var QWEN_RUNTIME_DIR takes priority.",
2278
+ showInDialog: false
2279
+ }
2280
+ }
2281
+ },
2282
+ agents: {
2283
+ type: "object",
2284
+ label: "Agents",
2285
+ category: "Advanced",
2286
+ requiresRestart: false,
2287
+ default: {},
2288
+ description: "Settings for multi-agent collaboration features (Arena, Team, Swarm).",
2289
+ showInDialog: false,
2290
+ properties: {
2291
+ displayMode: {
2292
+ type: "enum",
2293
+ label: "Display Mode",
2294
+ category: "Advanced",
2295
+ requiresRestart: false,
2296
+ default: void 0,
2297
+ description: 'Display mode for multi-agent sessions. Currently only "in-process" is supported.',
2298
+ showInDialog: false,
2299
+ options: [
2300
+ { value: "in-process", label: "In-process" }
2301
+ // { value: 'tmux', label: 'tmux' },
2302
+ // { value: 'iterm2', label: 'iTerm2' },
2303
+ ]
2304
+ },
2305
+ arena: {
2306
+ type: "object",
2307
+ label: "Arena",
2308
+ category: "Advanced",
2309
+ requiresRestart: false,
2310
+ default: {},
2311
+ description: "Settings for Arena (multi-model competitive execution).",
2312
+ showInDialog: false,
2313
+ properties: {
2314
+ worktreeBaseDir: {
2315
+ type: "string",
2316
+ label: "Worktree Base Directory",
2317
+ category: "Advanced",
2318
+ requiresRestart: true,
2319
+ default: void 0,
2320
+ description: "Custom base directory for Arena worktrees. Defaults to ~/.qwen/arena.",
2321
+ showInDialog: false
2322
+ },
2323
+ preserveArtifacts: {
2324
+ type: "boolean",
2325
+ label: "Preserve Arena Artifacts",
2326
+ category: "Advanced",
2327
+ requiresRestart: false,
2328
+ default: false,
2329
+ description: "When enabled, Arena worktrees and session state files are preserved after the session ends or the main agent exits.",
2330
+ showInDialog: true
2331
+ },
2332
+ maxRoundsPerAgent: {
2333
+ type: "number",
2334
+ label: "Max Rounds Per Agent",
2335
+ category: "Advanced",
2336
+ requiresRestart: false,
2337
+ default: void 0,
2338
+ description: "Maximum number of rounds (turns) each agent can execute. No limit if unset.",
2339
+ showInDialog: false
2340
+ },
2341
+ timeoutSeconds: {
2342
+ type: "number",
2343
+ label: "Timeout (seconds)",
2344
+ category: "Advanced",
2345
+ requiresRestart: false,
2346
+ default: void 0,
2347
+ description: "Total timeout in seconds for the Arena session. No limit if unset.",
2348
+ showInDialog: false
2349
+ }
2350
+ }
2351
+ },
2352
+ team: {
2353
+ type: "object",
2354
+ label: "Team",
2355
+ category: "Advanced",
2356
+ requiresRestart: false,
2357
+ default: {},
2358
+ description: "Settings for Agent Team (role-based collaborative execution). Reserved for future use.",
2359
+ showInDialog: false
2360
+ },
2361
+ swarm: {
2362
+ type: "object",
2363
+ label: "Swarm",
2364
+ category: "Advanced",
2365
+ requiresRestart: false,
2366
+ default: {},
2367
+ description: "Settings for Agent Swarm (parallel sub-agent execution). Reserved for future use.",
2368
+ showInDialog: false
2369
+ }
2370
+ }
2371
+ },
2372
+ disableAllHooks: {
2373
+ type: "boolean",
2374
+ label: "Disable All Hooks",
2375
+ category: "Advanced",
2376
+ requiresRestart: true,
2377
+ // Future enhancement: consider supporting mid-session toggle for better UX
2378
+ default: false,
2379
+ description: "Temporarily disable all hooks without deleting configurations. Default is false (hooks enabled).",
2380
+ showInDialog: false
2381
+ },
2382
+ stopHookBlockingCap: {
2383
+ type: "number",
2384
+ label: "Stop Hook Blocking Cap",
2385
+ category: "Advanced",
2386
+ requiresRestart: true,
2387
+ default: DEFAULT_STOP_HOOK_BLOCK_CAP,
2388
+ description: "Maximum consecutive blocking Stop/SubagentStop hook decisions before Qwen Code overrides the hook loop and ends the turn. Can be overridden by QWEN_CODE_STOP_HOOK_BLOCK_CAP.",
2389
+ // This is an advanced safety valve for runaway hook loops, not a common
2390
+ // interactive preference.
2391
+ showInDialog: false,
2392
+ jsonSchemaOverride: {
2393
+ type: "integer",
2394
+ minimum: 1,
2395
+ default: DEFAULT_STOP_HOOK_BLOCK_CAP
2396
+ }
2397
+ },
2398
+ hooks: {
2399
+ type: "object",
2400
+ label: "Hooks",
2401
+ category: "Advanced",
2402
+ requiresRestart: false,
2403
+ default: {},
2404
+ description: "Hook event configurations for extending CLI behavior at various lifecycle points.",
2405
+ showInDialog: false,
2406
+ properties: {
2407
+ UserPromptSubmit: {
2408
+ type: "array",
2409
+ label: "Before Agent Hooks",
2410
+ category: "Advanced",
2411
+ requiresRestart: false,
2412
+ default: [],
2413
+ description: "Hooks that execute before agent processing. Can modify prompts or inject context.",
2414
+ showInDialog: false,
2415
+ mergeStrategy: "concat" /* CONCAT */,
2416
+ items: HOOK_DEFINITION_ITEMS
2417
+ },
2418
+ UserPromptExpansion: {
2419
+ type: "array",
2420
+ label: "Prompt Expansion Hooks",
2421
+ category: "Advanced",
2422
+ requiresRestart: false,
2423
+ default: [],
2424
+ description: "Hooks that execute when a slash command expands into a prompt.",
2425
+ showInDialog: false,
2426
+ mergeStrategy: "concat" /* CONCAT */,
2427
+ items: HOOK_DEFINITION_ITEMS
2428
+ },
2429
+ Stop: {
2430
+ type: "array",
2431
+ label: "After Agent Hooks",
2432
+ category: "Advanced",
2433
+ requiresRestart: false,
2434
+ default: [],
2435
+ description: "Hooks that execute after agent processing. Can post-process responses or log interactions.",
2436
+ showInDialog: false,
2437
+ mergeStrategy: "concat" /* CONCAT */,
2438
+ items: HOOK_DEFINITION_ITEMS
2439
+ },
2440
+ Notification: {
2441
+ type: "array",
2442
+ label: "Notification Hooks",
2443
+ category: "Advanced",
2444
+ requiresRestart: false,
2445
+ default: [],
2446
+ description: "Hooks that execute when notifications are sent.",
2447
+ showInDialog: false,
2448
+ mergeStrategy: "concat" /* CONCAT */,
2449
+ items: HOOK_DEFINITION_ITEMS
2450
+ },
2451
+ PreToolUse: {
2452
+ type: "array",
2453
+ label: "Pre Tool Use Hooks",
2454
+ category: "Advanced",
2455
+ requiresRestart: false,
2456
+ default: [],
2457
+ description: "Hooks that execute before tool execution.",
2458
+ showInDialog: false,
2459
+ mergeStrategy: "concat" /* CONCAT */,
2460
+ items: HOOK_DEFINITION_ITEMS
2461
+ },
2462
+ PostToolUse: {
2463
+ type: "array",
2464
+ label: "Post Tool Use Hooks",
2465
+ category: "Advanced",
2466
+ requiresRestart: false,
2467
+ default: [],
2468
+ description: "Hooks that execute after successful tool execution.",
2469
+ showInDialog: false,
2470
+ mergeStrategy: "concat" /* CONCAT */,
2471
+ items: HOOK_DEFINITION_ITEMS
2472
+ },
2473
+ PostToolUseFailure: {
2474
+ type: "array",
2475
+ label: "Post Tool Use Failure Hooks",
2476
+ category: "Advanced",
2477
+ requiresRestart: false,
2478
+ default: [],
2479
+ description: "Hooks that execute when tool execution fails. ",
2480
+ showInDialog: false,
2481
+ mergeStrategy: "concat" /* CONCAT */,
2482
+ items: HOOK_DEFINITION_ITEMS
2483
+ },
2484
+ PostToolBatch: {
2485
+ type: "array",
2486
+ label: "Post Tool Batch Hooks",
2487
+ category: "Advanced",
2488
+ requiresRestart: false,
2489
+ default: [],
2490
+ description: "Hooks that execute once after all tool calls in a batch resolve.",
2491
+ showInDialog: false,
2492
+ mergeStrategy: "concat" /* CONCAT */,
2493
+ items: HOOK_DEFINITION_ITEMS
2494
+ },
2495
+ SessionStart: {
2496
+ type: "array",
2497
+ label: "Session Start Hooks",
2498
+ category: "Advanced",
2499
+ requiresRestart: false,
2500
+ default: [],
2501
+ description: "Hooks that execute when a new session starts or resumes.",
2502
+ showInDialog: false,
2503
+ mergeStrategy: "concat" /* CONCAT */,
2504
+ items: HOOK_DEFINITION_ITEMS
2505
+ },
2506
+ SessionEnd: {
2507
+ type: "array",
2508
+ label: "Session End Hooks",
2509
+ category: "Advanced",
2510
+ requiresRestart: false,
2511
+ default: [],
2512
+ description: "Hooks that execute when a session ends.",
2513
+ showInDialog: false,
2514
+ mergeStrategy: "concat" /* CONCAT */,
2515
+ items: HOOK_DEFINITION_ITEMS
2516
+ },
2517
+ PreCompact: {
2518
+ type: "array",
2519
+ label: "Pre Compact Hooks",
2520
+ category: "Advanced",
2521
+ requiresRestart: false,
2522
+ default: [],
2523
+ description: "Hooks that execute before conversation compaction.",
2524
+ showInDialog: false,
2525
+ mergeStrategy: "concat" /* CONCAT */,
2526
+ items: HOOK_DEFINITION_ITEMS
2527
+ },
2528
+ SubagentStart: {
2529
+ type: "array",
2530
+ label: "Subagent Start Hooks",
2531
+ category: "Advanced",
2532
+ requiresRestart: false,
2533
+ default: [],
2534
+ description: "Hooks that execute when a subagent (Task tool call) is started.",
2535
+ showInDialog: false,
2536
+ mergeStrategy: "concat" /* CONCAT */,
2537
+ items: HOOK_DEFINITION_ITEMS
2538
+ },
2539
+ SubagentStop: {
2540
+ type: "array",
2541
+ label: "Subagent Stop Hooks",
2542
+ category: "Advanced",
2543
+ requiresRestart: false,
2544
+ default: [],
2545
+ description: "Hooks that execute right before a subagent (Task tool call) concludes its response.",
2546
+ showInDialog: false,
2547
+ mergeStrategy: "concat" /* CONCAT */,
2548
+ items: HOOK_DEFINITION_ITEMS
2549
+ },
2550
+ PermissionRequest: {
2551
+ type: "array",
2552
+ label: "Permission Request Hooks",
2553
+ category: "Advanced",
2554
+ requiresRestart: false,
2555
+ default: [],
2556
+ description: "Hooks that execute when a permission dialog is displayed.",
2557
+ showInDialog: false,
2558
+ mergeStrategy: "concat" /* CONCAT */,
2559
+ items: HOOK_DEFINITION_ITEMS
2560
+ }
2561
+ }
2562
+ },
2563
+ experimental: {
2564
+ type: "object",
2565
+ label: "Experimental",
2566
+ category: "Experimental",
2567
+ requiresRestart: true,
2568
+ default: {},
2569
+ description: "Settings to enable experimental features.",
2570
+ showInDialog: false,
2571
+ properties: {
2572
+ cron: {
2573
+ type: "boolean",
2574
+ label: "Enable Cron/Loop Tools",
2575
+ category: "Experimental",
2576
+ requiresRestart: true,
2577
+ default: true,
2578
+ description: "Enable in-session cron/loop tools. When enabled, the model can create recurring prompts using cron_create, cron_list, and cron_delete tools. Can be disabled via QWEN_CODE_DISABLE_CRON=1 environment variable.",
2579
+ showInDialog: true
2580
+ },
2581
+ agentTeam: {
2582
+ type: "boolean",
2583
+ label: "Enable Agent Team",
2584
+ category: "Experimental",
2585
+ requiresRestart: true,
2586
+ default: false,
2587
+ description: "Enable agent team collaboration tools (experimental). When enabled, the model can create agent teams and coordinate work using team_create, team_delete, send_message, task_create, task_update, and task_list tools. Can also be enabled via QWEN_CODE_ENABLE_AGENT_TEAM=1 environment variable.",
2588
+ showInDialog: true
2589
+ },
2590
+ artifact: {
2591
+ type: "boolean",
2592
+ label: "Enable Artifacts",
2593
+ category: "Experimental",
2594
+ requiresRestart: true,
2595
+ default: false,
2596
+ description: "Enable the Artifact tool (experimental). When enabled, the model can publish a self-contained HTML page as an interactive Artifact and open it in the browser. Interactive, non-SDK sessions only. Can also be enabled via QWEN_CODE_ENABLE_ARTIFACT=1, or hard-disabled via QWEN_CODE_DISABLE_ARTIFACT=1.",
2597
+ showInDialog: true
2598
+ },
2599
+ emitToolUseSummaries: {
2600
+ type: "boolean",
2601
+ label: "Tool Use Summaries",
2602
+ category: "Experimental",
2603
+ requiresRestart: false,
2604
+ default: true,
2605
+ description: "Generate a short LLM-based label after each tool batch completes. In compact mode the label replaces the generic `Tool \xD7 N` header; in full mode it appears as a dim `\u25CF <label>` line below the tool group. Requires a fast model to be configured; runs in parallel with the next API call so latency is hidden. Currently affects interactive CLI rendering only \u2014 SDK / non-interactive emission of the `tool_use_summary` message is not yet wired (the message factory is exported for a follow-up PR). Can be overridden with QWEN_CODE_EMIT_TOOL_USE_SUMMARIES=0 or =1.",
2606
+ showInDialog: true
2607
+ }
2608
+ }
2609
+ },
2610
+ artifact: {
2611
+ type: "object",
2612
+ label: "Artifacts",
2613
+ category: "Experimental",
2614
+ requiresRestart: true,
2615
+ default: {},
2616
+ description: "Configuration for the experimental Artifact tool (enable it via experimental.artifact). Selects the publish backend and, for the host backend, the upload command and shareable URL template.",
2617
+ showInDialog: false,
2618
+ properties: {
2619
+ autoOpen: {
2620
+ type: "boolean",
2621
+ label: "Auto-open Artifacts",
2622
+ category: "Experimental",
2623
+ requiresRestart: true,
2624
+ default: true,
2625
+ description: "Open published artifacts in the browser automatically. Set to false to publish without launching a browser. QWEN_ARTIFACT_NO_AUTO_OPEN=1 overrides this setting.",
2626
+ showInDialog: false
2627
+ },
2628
+ publisher: {
2629
+ type: "enum",
2630
+ label: "Artifact Publisher",
2631
+ category: "Experimental",
2632
+ requiresRestart: true,
2633
+ default: "local",
2634
+ description: "Where artifacts are published: 'local' (a file:// page on disk, the default), 'host' (upload via artifact.host.uploadCommand and return a shareable link), or 'oss' (native Aliyun OSS upload).",
2635
+ showInDialog: false,
2636
+ options: [
2637
+ { value: "local", label: "Local (file://)" },
2638
+ { value: "host", label: "Host (shareable link)" },
2639
+ { value: "oss", label: "Aliyun OSS" }
2640
+ ]
2641
+ },
2642
+ host: {
2643
+ type: "object",
2644
+ label: "Artifact Host",
2645
+ category: "Experimental",
2646
+ requiresRestart: true,
2647
+ default: {},
2648
+ description: 'Host-backend config, used when artifact.publisher is "host".',
2649
+ showInDialog: false,
2650
+ properties: {
2651
+ uploadCommand: {
2652
+ type: "string",
2653
+ label: "Upload Command",
2654
+ category: "Experimental",
2655
+ requiresRestart: true,
2656
+ default: "",
2657
+ description: 'Command that uploads the artifact, run with execFile (no shell). {file} = local HTML path, {key} = remote object key. e.g. "aws s3 cp {file} s3://bucket/{key} --content-type text/html".',
2658
+ showInDialog: false
2659
+ },
2660
+ urlTemplate: {
2661
+ type: "string",
2662
+ label: "URL Template",
2663
+ category: "Experimental",
2664
+ requiresRestart: true,
2665
+ default: "",
2666
+ description: 'Shareable URL template; {key} is substituted. e.g. "https://bucket.example.com/{key}".',
2667
+ showInDialog: false
2668
+ },
2669
+ keyPrefix: {
2670
+ type: "string",
2671
+ label: "Key Prefix",
2672
+ category: "Experimental",
2673
+ requiresRestart: true,
2674
+ default: "artifacts",
2675
+ description: 'Remote key prefix; the object key is "{prefix}/{id}/index.html".',
2676
+ showInDialog: false
2677
+ }
2678
+ }
2679
+ },
2680
+ oss: {
2681
+ type: "object",
2682
+ label: "Artifact OSS",
2683
+ category: "Experimental",
2684
+ requiresRestart: true,
2685
+ default: {},
2686
+ description: 'Native Aliyun OSS backend, used when artifact.publisher is "oss". Credentials are read from OSS_ACCESS_KEY_ID / OSS_ACCESS_KEY_SECRET (or ALIBABA_CLOUD_*), never from settings.',
2687
+ showInDialog: false,
2688
+ properties: {
2689
+ bucket: {
2690
+ type: "string",
2691
+ label: "OSS Bucket",
2692
+ category: "Experimental",
2693
+ requiresRestart: true,
2694
+ default: "",
2695
+ description: "OSS bucket name.",
2696
+ showInDialog: false
2697
+ },
2698
+ endpoint: {
2699
+ type: "string",
2700
+ label: "OSS Endpoint",
2701
+ category: "Experimental",
2702
+ requiresRestart: true,
2703
+ default: "",
2704
+ description: 'OSS endpoint host, e.g. "oss-cn-hangzhou.aliyuncs.com".',
2705
+ showInDialog: false
2706
+ },
2707
+ keyPrefix: {
2708
+ type: "string",
2709
+ label: "Key Prefix",
2710
+ category: "Experimental",
2711
+ requiresRestart: true,
2712
+ default: "artifacts",
2713
+ description: 'Remote key prefix; the object key is "{prefix}/{id}/index.html".',
2714
+ showInDialog: false
2715
+ },
2716
+ acl: {
2717
+ type: "string",
2718
+ label: "Object ACL",
2719
+ category: "Experimental",
2720
+ requiresRestart: true,
2721
+ default: "public-read",
2722
+ description: 'Object ACL applied on upload. "public-read" (default) makes the link shareable.',
2723
+ showInDialog: false
2724
+ },
2725
+ publicBaseUrl: {
2726
+ type: "string",
2727
+ label: "Public Base URL",
2728
+ category: "Experimental",
2729
+ requiresRestart: true,
2730
+ default: "",
2731
+ description: 'Optional CDN / custom-domain base for the returned URL. Upload still goes through endpoint. e.g. "https://cdn.example.com".',
2732
+ showInDialog: false
2733
+ }
2734
+ }
2735
+ }
2736
+ }
2737
+ },
2738
+ worktree: {
2739
+ type: "object",
2740
+ label: "Worktree",
2741
+ category: "Advanced",
2742
+ requiresRestart: false,
2743
+ default: {},
2744
+ description: 'Configuration for general-purpose git worktrees created by the CLI (the `enter_worktree` tool, the `agent isolation: "worktree"` parameter, and the startup `--worktree` flag). Does NOT affect Agent Arena worktrees \u2014 see `agents.arena.worktreeBaseDir` for those.',
2745
+ showInDialog: false,
2746
+ properties: {
2747
+ symlinkDirectories: {
2748
+ type: "array",
2749
+ label: "Symlink Directories Into Worktrees",
2750
+ category: "Advanced",
2751
+ requiresRestart: false,
2752
+ default: void 0,
2753
+ description: "Directories under the main repository to symlink into every general-purpose worktree on creation. Useful for sharing large opt-in dirs like `node_modules` so the model can run tests / builds inside the worktree without a fresh install. Paths must be relative to the repo root; absolute paths, anything containing `..`, and any path inside `.git` or `.qwen` (the CLI-managed metadata tree, which contains the worktrees directory itself) are rejected. Missing source dirs and existing destination paths are silently skipped (no overwrite, no failure).",
2754
+ showInDialog: false
2755
+ }
2756
+ }
2757
+ }
2758
+ };
2759
+ function getSettingsSchema() {
2760
+ const schema = SETTINGS_SCHEMA;
2761
+ if (schema["general"]?.properties?.["language"]) {
2762
+ schema["general"].properties["language"].options = getLanguageSettingsOptions();
2763
+ }
2764
+ return SETTINGS_SCHEMA;
2765
+ }
2766
+ __name(getSettingsSchema, "getSettingsSchema");
2767
+
2768
+ // packages/cli/src/utils/settingsUtils.ts
2769
+ init_esbuild_shims();
2770
+ import * as fs2 from "node:fs";
2771
+
2772
+ // packages/cli/src/utils/languageUtils.ts
2773
+ init_esbuild_shims();
2774
+ import * as fs from "node:fs";
2775
+ import * as path from "node:path";
2776
+ var LLM_OUTPUT_LANGUAGE_RULE_FILENAME = "output-language.md";
2777
+ var LLM_OUTPUT_LANGUAGE_MARKER_PREFIX = "qwen-code:llm-output-language:";
2778
+ var OUTPUT_LANGUAGE_AUTO = "auto";
2779
+ function isAutoLanguage(value) {
2780
+ return !value || value.toLowerCase() === OUTPUT_LANGUAGE_AUTO;
2781
+ }
2782
+ __name(isAutoLanguage, "isAutoLanguage");
2783
+ function normalizeOutputLanguage(language) {
2784
+ const normalized = language.trim().replace(/_/g, "-").toLowerCase();
2785
+ const knownLanguageName = SUPPORTED_LANGUAGES.find(
2786
+ (supportedLanguage) => supportedLanguage.fullName.toLowerCase() === normalized
2787
+ );
2788
+ if (knownLanguageName) {
2789
+ return knownLanguageName.fullName;
2790
+ }
2791
+ const knownLocaleCode = SUPPORTED_LANGUAGES.some((supportedLanguage) => {
2792
+ const code = supportedLanguage.code.toLowerCase();
2793
+ const id = supportedLanguage.id.toLowerCase();
2794
+ return normalized === code || normalized === id || normalized.startsWith(`${code}-`) || normalized.startsWith(`${id}-`) || normalized.startsWith(`${code}.`) || normalized.startsWith(`${id}.`) || normalized.startsWith(`${code}@`) || normalized.startsWith(`${id}@`);
2795
+ });
2796
+ if (!knownLocaleCode) {
2797
+ return language;
2798
+ }
2799
+ return getLanguageNameFromLocale(normalized);
2800
+ }
2801
+ __name(normalizeOutputLanguage, "normalizeOutputLanguage");
2802
+ function resolveOutputLanguage(value) {
2803
+ if (isAutoLanguage(value)) {
2804
+ const detectedLocale = detectSystemLanguage();
2805
+ return getLanguageNameFromLocale(detectedLocale);
2806
+ }
2807
+ return normalizeOutputLanguage(value);
2808
+ }
2809
+ __name(resolveOutputLanguage, "resolveOutputLanguage");
2810
+ function getOutputLanguageFilePath() {
2811
+ return path.join(
2812
+ Storage.getGlobalQwenDir(),
2813
+ LLM_OUTPUT_LANGUAGE_RULE_FILENAME
2814
+ );
2815
+ }
2816
+ __name(getOutputLanguageFilePath, "getOutputLanguageFilePath");
2817
+ function sanitizeForMarker(language) {
2818
+ return language.replace(/[\r\n]/g, " ").replace(/--!?>/g, "").replace(/--/g, "");
2819
+ }
2820
+ __name(sanitizeForMarker, "sanitizeForMarker");
2821
+ function generateOutputLanguageFileContent(language) {
2822
+ const safeLanguage = sanitizeForMarker(language);
2823
+ return `# Output language preference: ${language}
2824
+ <!-- ${LLM_OUTPUT_LANGUAGE_MARKER_PREFIX} ${safeLanguage} -->
2825
+
2826
+ ## Rule
2827
+ You MUST always respond in **${language}** regardless of the user's input language.
2828
+ This is a mandatory requirement, not a preference.
2829
+
2830
+ ## Exception
2831
+ If the user **explicitly** requests a response in a specific language (e.g., "please reply in English", "\u7528\u4E2D\u6587\u56DE\u7B54"), switch to the user's requested language for the remainder of the conversation.
2832
+
2833
+ ## Keep technical artifacts unchanged
2834
+ Do **not** translate or rewrite:
2835
+ - Code blocks, CLI commands, file paths, stack traces, logs, JSON keys, identifiers
2836
+ - Exact quoted text from the user (keep quotes verbatim)
2837
+
2838
+ ## Tool / system outputs
2839
+ Raw tool/system outputs may contain fixed-format English. Preserve them verbatim, and if needed, add a short **${language}** explanation below.
2840
+ `;
2841
+ }
2842
+ __name(generateOutputLanguageFileContent, "generateOutputLanguageFileContent");
2843
+ function parseOutputLanguageFromContent(content) {
2844
+ const markerRegex = new RegExp(
2845
+ String.raw`<!--\s*${LLM_OUTPUT_LANGUAGE_MARKER_PREFIX}\s*(.*?)\s*-->`,
2846
+ "i"
2847
+ );
2848
+ const markerMatch = content.match(markerRegex);
2849
+ if (markerMatch?.[1]?.trim()) {
2850
+ return markerMatch[1].trim();
2851
+ }
2852
+ const headingMatch = content.match(
2853
+ /^#.*?CRITICAL:\s*(.*?)\s+Output Language Rule\b/im
2854
+ );
2855
+ if (headingMatch?.[1]?.trim()) {
2856
+ return headingMatch[1].trim();
2857
+ }
2858
+ return null;
2859
+ }
2860
+ __name(parseOutputLanguageFromContent, "parseOutputLanguageFromContent");
2861
+ function readOutputLanguageFromFile() {
2862
+ const filePath = getOutputLanguageFilePath();
2863
+ if (!fs.existsSync(filePath)) {
2864
+ return null;
2865
+ }
2866
+ try {
2867
+ const content = fs.readFileSync(filePath, "utf-8");
2868
+ return parseOutputLanguageFromContent(content);
2869
+ } catch {
2870
+ return null;
2871
+ }
2872
+ }
2873
+ __name(readOutputLanguageFromFile, "readOutputLanguageFromFile");
2874
+ function writeOutputLanguageFile(language, targetPath) {
2875
+ const filePath = targetPath ?? getOutputLanguageFilePath();
2876
+ const content = generateOutputLanguageFileContent(language);
2877
+ const dir = path.dirname(filePath);
2878
+ fs.mkdirSync(dir, { recursive: true });
2879
+ fs.writeFileSync(filePath, content, "utf-8");
2880
+ }
2881
+ __name(writeOutputLanguageFile, "writeOutputLanguageFile");
2882
+ function updateOutputLanguageFile(settingValue, targetPath) {
2883
+ const resolved = resolveOutputLanguage(settingValue);
2884
+ writeOutputLanguageFile(resolved, targetPath);
2885
+ }
2886
+ __name(updateOutputLanguageFile, "updateOutputLanguageFile");
2887
+ function writeOutputLanguageAndRegisterPath(settingValue, config) {
2888
+ const targetPath = config?.getOutputLanguageFilePath();
2889
+ updateOutputLanguageFile(settingValue, targetPath);
2890
+ if (!targetPath) {
2891
+ config?.setOutputLanguageFilePath(getOutputLanguageFilePath());
2892
+ }
2893
+ }
2894
+ __name(writeOutputLanguageAndRegisterPath, "writeOutputLanguageAndRegisterPath");
2895
+ function initializeLlmOutputLanguage(outputLanguage) {
2896
+ const currentFileLanguage = readOutputLanguageFromFile();
2897
+ if (currentFileLanguage) {
2898
+ return;
2899
+ }
2900
+ const resolved = resolveOutputLanguage(outputLanguage);
2901
+ writeOutputLanguageFile(resolved);
2902
+ }
2903
+ __name(initializeLlmOutputLanguage, "initializeLlmOutputLanguage");
2904
+
2905
+ // packages/cli/src/utils/settingsUtils.ts
2906
+ function flattenSchema(schema, prefix = "") {
2907
+ let result = {};
2908
+ for (const key in schema) {
2909
+ const newKey = prefix ? `${prefix}.${key}` : key;
2910
+ const definition = schema[key];
2911
+ result[newKey] = { ...definition, key: newKey };
2912
+ if (definition.properties) {
2913
+ result = { ...result, ...flattenSchema(definition.properties, newKey) };
2914
+ }
2915
+ }
2916
+ return result;
2917
+ }
2918
+ __name(flattenSchema, "flattenSchema");
2919
+ var _FLATTENED_SCHEMA;
2920
+ function getFlattenedSchema() {
2921
+ return _FLATTENED_SCHEMA ?? (_FLATTENED_SCHEMA = flattenSchema(getSettingsSchema()));
2922
+ }
2923
+ __name(getFlattenedSchema, "getFlattenedSchema");
2924
+ function getSettingDefinition(key) {
2925
+ return getFlattenedSchema()[key];
2926
+ }
2927
+ __name(getSettingDefinition, "getSettingDefinition");
2928
+ function requiresRestart(key) {
2929
+ return getFlattenedSchema()[key]?.requiresRestart ?? false;
2930
+ }
2931
+ __name(requiresRestart, "requiresRestart");
2932
+ function getDefaultValue(key) {
2933
+ return getFlattenedSchema()[key]?.default;
2934
+ }
2935
+ __name(getDefaultValue, "getDefaultValue");
2936
+ function getNestedValue(obj, path4) {
2937
+ const [first, ...rest] = path4;
2938
+ if (!first || !(first in obj)) {
2939
+ return void 0;
2940
+ }
2941
+ const value = obj[first];
2942
+ if (rest.length === 0) {
2943
+ return value;
2944
+ }
2945
+ if (value && typeof value === "object" && value !== null) {
2946
+ return getNestedValue(value, rest);
2947
+ }
2948
+ return void 0;
2949
+ }
2950
+ __name(getNestedValue, "getNestedValue");
2951
+ function getNestedProperty(obj, path4) {
2952
+ return getNestedValue(obj, path4.split("."));
2953
+ }
2954
+ __name(getNestedProperty, "getNestedProperty");
2955
+ function getEffectiveValue(key, settings, mergedSettings) {
2956
+ const definition = getSettingDefinition(key);
2957
+ if (!definition) {
2958
+ return void 0;
2959
+ }
2960
+ const path4 = key.split(".");
2961
+ let value = getNestedValue(settings, path4);
2962
+ if (value !== void 0) {
2963
+ return value;
2964
+ }
2965
+ value = getNestedValue(mergedSettings, path4);
2966
+ if (value !== void 0) {
2967
+ return value;
2968
+ }
2969
+ return definition.default;
2970
+ }
2971
+ __name(getEffectiveValue, "getEffectiveValue");
2972
+ var SETTINGS_DIALOG_ORDER = [
2973
+ // Workflow Control - most impactful setting
2974
+ "tools.approvalMode",
2975
+ // Localization - users often set this first
2976
+ "general.language",
2977
+ "general.outputLanguage",
2978
+ // Theme
2979
+ "ui.theme",
2980
+ // Editor/Shell Experience
2981
+ "general.vimMode",
2982
+ "tools.shell.enableInteractiveShell",
2983
+ // Display Preferences
2984
+ "general.preferredEditor",
2985
+ "ide.enabled",
2986
+ "ui.showLineNumbers",
2987
+ "ui.hideTips",
2988
+ "general.terminalBell",
2989
+ "ui.enableWelcomeBack",
2990
+ // Git Behavior
2991
+ "general.gitCoAuthor.commit",
2992
+ "general.gitCoAuthor.pr",
2993
+ // File Filtering
2994
+ "context.fileFiltering.respectGitIgnore",
2995
+ "context.fileFiltering.respectQwenIgnore",
2996
+ // System Settings - rarely changed
2997
+ "general.disableAutoUpdate",
2998
+ // Privacy
2999
+ "privacy.usageStatisticsEnabled"
3000
+ ];
3001
+ var MAX_SETTING_STRING_VALUE_LENGTH = 1024;
3002
+ function validateSettingValue(def, value) {
3003
+ switch (def.type) {
3004
+ case "boolean":
3005
+ if (typeof value !== "boolean") return "Value must be a boolean";
3006
+ break;
3007
+ case "number":
3008
+ if (typeof value !== "number" || !Number.isFinite(value))
3009
+ return "Value must be a finite number";
3010
+ if (def.minimum !== void 0 && value < def.minimum)
3011
+ return `Value must be >= ${def.minimum}`;
3012
+ if (def.maximum !== void 0 && value > def.maximum)
3013
+ return `Value must be <= ${def.maximum}`;
3014
+ break;
3015
+ case "string":
3016
+ if (typeof value !== "string") return "Value must be a string";
3017
+ if (value.length > MAX_SETTING_STRING_VALUE_LENGTH)
3018
+ return `Value exceeds ${MAX_SETTING_STRING_VALUE_LENGTH}-character limit`;
3019
+ break;
3020
+ case "enum":
3021
+ if (!def.options?.some((opt) => opt.value === value)) {
3022
+ const allowed = def.options?.map((o) => o.value).join(", ") ?? "";
3023
+ return `Value must be one of: ${allowed}`;
3024
+ }
3025
+ break;
3026
+ default:
3027
+ return `Settings of type '${def.type}' cannot be modified via this API`;
3028
+ }
3029
+ return void 0;
3030
+ }
3031
+ __name(validateSettingValue, "validateSettingValue");
3032
+ function getDialogSettingKeys() {
3033
+ const dialogSettings = Object.values(getFlattenedSchema()).filter((definition) => definition.showInDialog === true).map((definition) => definition.key);
3034
+ return dialogSettings.sort((a, b) => {
3035
+ const indexA = SETTINGS_DIALOG_ORDER.indexOf(a);
3036
+ const indexB = SETTINGS_DIALOG_ORDER.indexOf(b);
3037
+ if (indexA !== -1 && indexB !== -1) {
3038
+ return indexA - indexB;
3039
+ }
3040
+ if (indexA !== -1) return -1;
3041
+ if (indexB !== -1) return 1;
3042
+ return 0;
3043
+ });
3044
+ }
3045
+ __name(getDialogSettingKeys, "getDialogSettingKeys");
3046
+ function settingExistsInScope(key, scopeSettings) {
3047
+ const path4 = key.split(".");
3048
+ const value = getNestedValue(scopeSettings, path4);
3049
+ return value !== void 0;
3050
+ }
3051
+ __name(settingExistsInScope, "settingExistsInScope");
3052
+ function pathHasUnsafeSegment(keys) {
3053
+ for (const key of keys) {
3054
+ if (key === "__proto__" || key === "constructor" || key === "prototype") {
3055
+ return true;
3056
+ }
3057
+ }
3058
+ return false;
3059
+ }
3060
+ __name(pathHasUnsafeSegment, "pathHasUnsafeSegment");
3061
+ function setNestedPropertyForce(obj, path4, value) {
3062
+ const keys = path4.split(".");
3063
+ if (pathHasUnsafeSegment(keys)) return;
3064
+ const lastKey = keys.pop();
3065
+ if (!lastKey) return;
3066
+ let current = obj;
3067
+ for (const key of keys) {
3068
+ if (!current[key] || typeof current[key] !== "object") {
3069
+ current[key] = {};
3070
+ }
3071
+ current = current[key];
3072
+ }
3073
+ current[lastKey] = value;
3074
+ }
3075
+ __name(setNestedPropertyForce, "setNestedPropertyForce");
3076
+ function setNestedPropertySafe(obj, path4, value) {
3077
+ const keys = path4.split(".");
3078
+ if (pathHasUnsafeSegment(keys)) return;
3079
+ const lastKey = keys.pop();
3080
+ if (!lastKey) return;
3081
+ let current = obj;
3082
+ for (const key of keys) {
3083
+ if (current[key] === void 0) {
3084
+ current[key] = {};
3085
+ }
3086
+ const next = current[key];
3087
+ if (typeof next === "object" && next !== null) {
3088
+ current = next;
3089
+ } else {
3090
+ return;
3091
+ }
3092
+ }
3093
+ current[lastKey] = value;
3094
+ }
3095
+ __name(setNestedPropertySafe, "setNestedPropertySafe");
3096
+ function deleteNestedPropertySafe(obj, path4) {
3097
+ const keys = path4.split(".");
3098
+ const lastKey = keys.pop();
3099
+ if (!lastKey) return;
3100
+ let current = obj;
3101
+ for (const key of keys) {
3102
+ const next = current[key];
3103
+ if (typeof next !== "object" || next === null) {
3104
+ return;
3105
+ }
3106
+ current = next;
3107
+ }
3108
+ delete current[lastKey];
3109
+ }
3110
+ __name(deleteNestedPropertySafe, "deleteNestedPropertySafe");
3111
+ function setPendingSettingValue(key, value, pendingSettings) {
3112
+ const newSettings = JSON.parse(JSON.stringify(pendingSettings));
3113
+ setNestedPropertyForce(newSettings, key, value);
3114
+ return newSettings;
3115
+ }
3116
+ __name(setPendingSettingValue, "setPendingSettingValue");
3117
+ function setPendingSettingValueAny(key, value, pendingSettings) {
3118
+ const newSettings = structuredClone(pendingSettings);
3119
+ setNestedPropertyForce(newSettings, key, value);
3120
+ return newSettings;
3121
+ }
3122
+ __name(setPendingSettingValueAny, "setPendingSettingValueAny");
3123
+ function getRestartRequiredFromModified(modifiedSettings) {
3124
+ return Array.from(modifiedSettings).filter((key) => requiresRestart(key));
3125
+ }
3126
+ __name(getRestartRequiredFromModified, "getRestartRequiredFromModified");
3127
+ function saveModifiedSettings(modifiedSettings, pendingSettings, loadedSettings, scope) {
3128
+ modifiedSettings.forEach((settingKey) => {
3129
+ const path4 = settingKey.split(".");
3130
+ const value = getNestedValue(
3131
+ pendingSettings,
3132
+ path4
3133
+ );
3134
+ const existsInOriginalFile = settingExistsInScope(
3135
+ settingKey,
3136
+ loadedSettings.forScope(scope).settings
3137
+ );
3138
+ if (value === void 0) {
3139
+ if (existsInOriginalFile) {
3140
+ loadedSettings.setValue(scope, settingKey, void 0);
3141
+ }
3142
+ return;
3143
+ }
3144
+ const isDefaultValue2 = value === getDefaultValue(settingKey);
3145
+ if (existsInOriginalFile || !isDefaultValue2) {
3146
+ loadedSettings.setValue(scope, settingKey, value);
3147
+ }
3148
+ });
3149
+ }
3150
+ __name(saveModifiedSettings, "saveModifiedSettings");
3151
+ function getDisplayValue(key, settings, _mergedSettings, modifiedSettings, pendingSettings) {
3152
+ const definition = getSettingDefinition(key);
3153
+ let value;
3154
+ if (pendingSettings && settingExistsInScope(key, pendingSettings)) {
3155
+ value = getEffectiveValue(key, pendingSettings, {});
3156
+ } else if (settingExistsInScope(key, settings)) {
3157
+ value = getEffectiveValue(key, settings, {});
3158
+ } else {
3159
+ value = getDefaultValue(key);
3160
+ }
3161
+ let valueString = String(value);
3162
+ if (key === "general.outputLanguage" && isAutoLanguage(value)) {
3163
+ valueString = t("Auto (detect from system)");
3164
+ } else if (definition?.type === "enum" && definition.options) {
3165
+ const option = definition.options?.find((option2) => option2.value === value);
3166
+ if (option?.label) {
3167
+ valueString = t(option.label) || option.label;
3168
+ } else {
3169
+ valueString = `${value}`;
3170
+ }
3171
+ }
3172
+ const defaultValue = getDefaultValue(key);
3173
+ const isChangedFromDefault = value !== defaultValue;
3174
+ const isInModifiedSettings = modifiedSettings.has(key);
3175
+ if (settingExistsInScope(key, settings) || isInModifiedSettings) {
3176
+ return `${valueString}*`;
3177
+ }
3178
+ if (isChangedFromDefault || isInModifiedSettings) {
3179
+ return `${valueString}*`;
3180
+ }
3181
+ return valueString;
3182
+ }
3183
+ __name(getDisplayValue, "getDisplayValue");
3184
+ function isDefaultValue(key, settings) {
3185
+ return !settingExistsInScope(key, settings);
3186
+ }
3187
+ __name(isDefaultValue, "isDefaultValue");
3188
+ function backupSettingsFile(filePath) {
3189
+ try {
3190
+ if (fs2.existsSync(filePath)) {
3191
+ const backupPath = `${filePath}.orig`;
3192
+ fs2.copyFileSync(filePath, backupPath);
3193
+ return true;
3194
+ }
3195
+ } catch (_e) {
3196
+ }
3197
+ return false;
3198
+ }
3199
+ __name(backupSettingsFile, "backupSettingsFile");
3200
+ function restoreSettingsFromBackup(filePath) {
3201
+ try {
3202
+ const backupPath = `${filePath}.orig`;
3203
+ if (fs2.existsSync(backupPath)) {
3204
+ fs2.copyFileSync(backupPath, filePath);
3205
+ fs2.unlinkSync(backupPath);
3206
+ return true;
3207
+ }
3208
+ } catch (err) {
3209
+ console.error(
3210
+ `[settingsUtils] restoreSettingsFromBackup(${filePath}) failed:`,
3211
+ err
3212
+ );
3213
+ }
3214
+ return false;
3215
+ }
3216
+ __name(restoreSettingsFromBackup, "restoreSettingsFromBackup");
3217
+ function cleanupSettingsBackup(filePath) {
3218
+ try {
3219
+ const backupPath = `${filePath}.orig`;
3220
+ if (fs2.existsSync(backupPath)) {
3221
+ fs2.unlinkSync(backupPath);
3222
+ }
3223
+ } catch (_e) {
3224
+ }
3225
+ }
3226
+ __name(cleanupSettingsBackup, "cleanupSettingsBackup");
3227
+
3228
+ // packages/cli/src/utils/deepMerge.ts
3229
+ init_esbuild_shims();
3230
+ function isPlainObject(item) {
3231
+ return !!item && typeof item === "object" && !Array.isArray(item);
3232
+ }
3233
+ __name(isPlainObject, "isPlainObject");
3234
+ function mergeRecursively(target, source, getMergeStrategyForPath2, path4 = []) {
3235
+ for (const key of Object.keys(source)) {
3236
+ if (key === "__proto__" || key === "constructor" || key === "prototype") {
3237
+ continue;
3238
+ }
3239
+ const newPath = [...path4, key];
3240
+ const srcValue = source[key];
3241
+ const objValue = target[key];
3242
+ const mergeStrategy = getMergeStrategyForPath2(newPath);
3243
+ if (mergeStrategy === "shallow_merge" /* SHALLOW_MERGE */ && objValue && srcValue) {
3244
+ const obj1 = typeof objValue === "object" && objValue !== null ? objValue : {};
3245
+ const obj2 = typeof srcValue === "object" && srcValue !== null ? srcValue : {};
3246
+ target[key] = { ...obj1, ...obj2 };
3247
+ continue;
3248
+ }
3249
+ if (Array.isArray(objValue)) {
3250
+ const srcArray = Array.isArray(srcValue) ? srcValue : [srcValue];
3251
+ if (mergeStrategy === "concat" /* CONCAT */) {
3252
+ target[key] = objValue.concat(srcArray);
3253
+ continue;
3254
+ }
3255
+ if (mergeStrategy === "union" /* UNION */) {
3256
+ target[key] = [...new Set(objValue.concat(srcArray))];
3257
+ continue;
3258
+ }
3259
+ }
3260
+ if (isPlainObject(objValue) && isPlainObject(srcValue)) {
3261
+ mergeRecursively(objValue, srcValue, getMergeStrategyForPath2, newPath);
3262
+ } else if (isPlainObject(srcValue)) {
3263
+ target[key] = {};
3264
+ mergeRecursively(
3265
+ target[key],
3266
+ srcValue,
3267
+ getMergeStrategyForPath2,
3268
+ newPath
3269
+ );
3270
+ } else {
3271
+ target[key] = srcValue;
3272
+ }
3273
+ }
3274
+ return target;
3275
+ }
3276
+ __name(mergeRecursively, "mergeRecursively");
3277
+ function customDeepMerge(getMergeStrategyForPath2, ...sources) {
3278
+ const result = {};
3279
+ for (const source of sources) {
3280
+ if (source) {
3281
+ mergeRecursively(result, source, getMergeStrategyForPath2);
3282
+ }
3283
+ }
3284
+ return result;
3285
+ }
3286
+ __name(customDeepMerge, "customDeepMerge");
3287
+
3288
+ // packages/cli/src/config/migration/index.ts
3289
+ init_esbuild_shims();
3290
+
3291
+ // packages/cli/src/config/migration/scheduler.ts
3292
+ init_esbuild_shims();
3293
+ var debugLogger = createDebugLogger("SETTINGS_MIGRATION");
3294
+ function formatScope(scope) {
3295
+ if (scope === "SystemDefaults") {
3296
+ return "system default";
3297
+ }
3298
+ return scope.toLowerCase();
3299
+ }
3300
+ __name(formatScope, "formatScope");
3301
+ var MigrationScheduler = class {
3302
+ /**
3303
+ * Creates a new MigrationScheduler with the given migrations.
3304
+ *
3305
+ * @param migrations - Array of migrations in execution order (typically ascending version)
3306
+ * @param scope - The scope of settings being migrated
3307
+ */
3308
+ constructor(migrations, scope) {
3309
+ this.migrations = migrations;
3310
+ this.scope = scope;
3311
+ }
3312
+ static {
3313
+ __name(this, "MigrationScheduler");
3314
+ }
3315
+ /**
3316
+ * Executes the migration chain on the given settings.
3317
+ *
3318
+ * Iterates through all registered migrations in order. For each migration:
3319
+ * 1. Calls `shouldMigrate` with the current settings
3320
+ * 2. If true, calls `migrate` to transform the settings
3321
+ * 3. Records the execution
3322
+ *
3323
+ * The scheduler itself has no version awareness - all version detection
3324
+ * is delegated to the individual migrations.
3325
+ *
3326
+ * @param settings - The settings object to migrate
3327
+ * @returns MigrationResult containing the final settings, version, and execution log
3328
+ */
3329
+ migrate(settings) {
3330
+ debugLogger.debug("MigrationScheduler: Starting migration chain");
3331
+ let current = settings;
3332
+ const executed = [];
3333
+ const allWarnings = [];
3334
+ for (const migration of this.migrations) {
3335
+ try {
3336
+ if (migration.shouldMigrate(current)) {
3337
+ debugLogger.debug(
3338
+ `MigrationScheduler: Executing migration ${migration.fromVersion} \u2192 ${migration.toVersion}`
3339
+ );
3340
+ const formattedScope = formatScope(this.scope);
3341
+ const result = migration.migrate(current, formattedScope);
3342
+ current = result.settings;
3343
+ allWarnings.push(...result.warnings);
3344
+ executed.push({
3345
+ fromVersion: migration.fromVersion,
3346
+ toVersion: migration.toVersion
3347
+ });
3348
+ debugLogger.debug(
3349
+ `MigrationScheduler: Migration ${migration.fromVersion} \u2192 ${migration.toVersion} completed successfully`
3350
+ );
3351
+ }
3352
+ } catch (error) {
3353
+ debugLogger.error(
3354
+ `MigrationScheduler: Migration ${migration.fromVersion} \u2192 ${migration.toVersion} failed:`,
3355
+ error
3356
+ );
3357
+ throw error;
3358
+ }
3359
+ }
3360
+ const finalVersion = current["$version"] ?? 1;
3361
+ debugLogger.debug(
3362
+ `MigrationScheduler: Migration chain complete. Final version: ${finalVersion}, Executed: ${executed.length} migrations`
3363
+ );
3364
+ return {
3365
+ settings: current,
3366
+ finalVersion,
3367
+ executedMigrations: executed,
3368
+ warnings: allWarnings
3369
+ };
3370
+ }
3371
+ };
3372
+
3373
+ // packages/cli/src/config/migration/versions/v1-to-v2.ts
3374
+ init_esbuild_shims();
3375
+ var V1ToV2Migration = class {
3376
+ static {
3377
+ __name(this, "V1ToV2Migration");
3378
+ }
3379
+ fromVersion = 1;
3380
+ toVersion = 2;
3381
+ /**
3382
+ * Determines whether this migration should execute.
3383
+ *
3384
+ * Decision strategy:
3385
+ * - Hard-stop when `$version` is a number >= 2 (already V2+).
3386
+ * - Otherwise, scan indicator keys and trigger only when at least one indicator is
3387
+ * still in legacy top-level shape (primitive/array/null).
3388
+ *
3389
+ * Mixed-shape tolerance:
3390
+ * - Files that are partially migrated are supported; V2-like object-valued indicators
3391
+ * are ignored while legacy-shaped indicators can still trigger migration.
3392
+ */
3393
+ shouldMigrate(settings) {
3394
+ if (typeof settings !== "object" || settings === null) {
3395
+ return false;
3396
+ }
3397
+ const s = settings;
3398
+ const version = s["$version"];
3399
+ if (typeof version === "number" && version >= 2) {
3400
+ return false;
3401
+ }
3402
+ return V1_INDICATOR_KEYS.some((key) => {
3403
+ if (!(key in s)) {
3404
+ return false;
3405
+ }
3406
+ const value = s[key];
3407
+ if (typeof value === "object" && value !== null && !Array.isArray(value)) {
3408
+ return false;
3409
+ }
3410
+ return true;
3411
+ });
3412
+ }
3413
+ /**
3414
+ * Performs non-destructive V1 -> V2 transformation.
3415
+ *
3416
+ * Detailed strategy:
3417
+ * 1) Relocate known V1 keys using `V1_TO_V2_MIGRATION_MAP`.
3418
+ * - If a source value is already an object and maps to a child path of itself
3419
+ * (partial V2 shape), merge child properties into target path.
3420
+ * 2) Relocate disable* keys into V2 disable* locations.
3421
+ * - Consolidated keys (`disableAutoUpdate`, `disableUpdateNag`): normalize to
3422
+ * boolean with stable-compatible presence semantics (`value === true`).
3423
+ * - Other disable* keys: migrate only boolean values.
3424
+ * 3) Preserve `mcpServers` top-level placement.
3425
+ * 4) Carry over remaining keys:
3426
+ * - If a key is parent of migrated nested paths, merge unprocessed object children.
3427
+ * - If parent value is non-object, preserve that scalar/array/null as-is.
3428
+ * - Otherwise copy untouched key/value.
3429
+ * 5) Stamp `$version = 2`.
3430
+ *
3431
+ * The method is pure with respect to input mutation.
3432
+ */
3433
+ migrate(settings, _scope) {
3434
+ if (typeof settings !== "object" || settings === null) {
3435
+ throw new Error("Settings must be an object");
3436
+ }
3437
+ const source = settings;
3438
+ const result = {};
3439
+ const processedKeys = /* @__PURE__ */ new Set();
3440
+ const warnings = [];
3441
+ for (const [v1Key, v2Path] of Object.entries(V1_TO_V2_MIGRATION_MAP)) {
3442
+ if (v1Key in source) {
3443
+ const value = source[v1Key];
3444
+ if (V2_CONTAINER_KEYS.has(v1Key) && typeof value === "object" && value !== null && !Array.isArray(value)) {
3445
+ result[v1Key] = value;
3446
+ processedKeys.add(v1Key);
3447
+ continue;
3448
+ }
3449
+ if (typeof value === "object" && value !== null && !Array.isArray(value) && v2Path.startsWith(v1Key + ".")) {
3450
+ for (const [nestedKey, nestedValue] of Object.entries(value)) {
3451
+ setNestedPropertySafe(
3452
+ result,
3453
+ `${v2Path}.${nestedKey}`,
3454
+ nestedValue
3455
+ );
3456
+ }
3457
+ } else {
3458
+ setNestedPropertySafe(result, v2Path, value);
3459
+ }
3460
+ processedKeys.add(v1Key);
3461
+ }
3462
+ }
3463
+ for (const [v1Key, v2Path] of Object.entries(
3464
+ V1_TO_V2_PRESERVE_DISABLE_MAP
3465
+ )) {
3466
+ if (v1Key in source) {
3467
+ const value = source[v1Key];
3468
+ if (CONSOLIDATED_DISABLE_KEYS.has(v1Key)) {
3469
+ setNestedPropertySafe(result, v2Path, value === true);
3470
+ } else if (typeof value === "boolean") {
3471
+ setNestedPropertySafe(result, v2Path, value);
3472
+ }
3473
+ processedKeys.add(v1Key);
3474
+ }
3475
+ }
3476
+ if ("mcpServers" in source) {
3477
+ result["mcpServers"] = source["mcpServers"];
3478
+ processedKeys.add("mcpServers");
3479
+ }
3480
+ for (const key of Object.keys(source)) {
3481
+ if (!processedKeys.has(key)) {
3482
+ const isParentOfMigratedPath = Array.from(processedKeys).some(
3483
+ (processedKey) => {
3484
+ const v2Path = V1_TO_V2_MIGRATION_MAP[processedKey] || V1_TO_V2_PRESERVE_DISABLE_MAP[processedKey];
3485
+ if (!v2Path) return false;
3486
+ return v2Path.startsWith(key + ".");
3487
+ }
3488
+ );
3489
+ if (isParentOfMigratedPath) {
3490
+ const existingValue = source[key];
3491
+ if (typeof existingValue === "object" && existingValue !== null && !Array.isArray(existingValue)) {
3492
+ for (const [nestedKey, nestedValue] of Object.entries(
3493
+ existingValue
3494
+ )) {
3495
+ const fullNestedPath = `${key}.${nestedKey}`;
3496
+ const wasProcessed = Array.from(processedKeys).some(
3497
+ (processedKey) => {
3498
+ const v2Path = V1_TO_V2_MIGRATION_MAP[processedKey] || V1_TO_V2_PRESERVE_DISABLE_MAP[processedKey];
3499
+ return v2Path === fullNestedPath;
3500
+ }
3501
+ );
3502
+ if (!wasProcessed) {
3503
+ setNestedPropertySafe(result, fullNestedPath, nestedValue);
3504
+ }
3505
+ }
3506
+ } else {
3507
+ result[key] = source[key];
3508
+ }
3509
+ } else {
3510
+ result[key] = source[key];
3511
+ }
3512
+ }
3513
+ }
3514
+ result["$version"] = 2;
3515
+ return { settings: result, warnings };
3516
+ }
3517
+ };
3518
+ var v1ToV2Migration = new V1ToV2Migration();
3519
+
3520
+ // packages/cli/src/config/migration/versions/v2-to-v3.ts
3521
+ init_esbuild_shims();
3522
+ var V2_TO_V3_BOOLEAN_MAP = {
3523
+ "general.disableAutoUpdate": "general.enableAutoUpdate",
3524
+ "general.disableUpdateNag": "general.enableAutoUpdate",
3525
+ "ui.accessibility.disableLoadingPhrases": "ui.accessibility.enableLoadingPhrases",
3526
+ "context.fileFiltering.disableFuzzySearch": "context.fileFiltering.enableFuzzySearch",
3527
+ "model.generationConfig.disableCacheControl": "model.generationConfig.enableCacheControl"
3528
+ };
3529
+ var CONSOLIDATED_V2_PATHS = {
3530
+ "general.enableAutoUpdate": [
3531
+ "general.disableAutoUpdate",
3532
+ "general.disableUpdateNag"
3533
+ ]
3534
+ };
3535
+ function normalizeDisableValue(value) {
3536
+ if (value === void 0) {
3537
+ return { isPresent: false, isValid: false };
3538
+ }
3539
+ if (typeof value === "boolean") {
3540
+ return { isPresent: true, isValid: true, booleanValue: value };
3541
+ }
3542
+ if (typeof value === "string") {
3543
+ const normalized = value.trim().toLowerCase();
3544
+ if (normalized === "true") {
3545
+ return { isPresent: true, isValid: true, booleanValue: true };
3546
+ }
3547
+ if (normalized === "false") {
3548
+ return { isPresent: true, isValid: true, booleanValue: false };
3549
+ }
3550
+ }
3551
+ return { isPresent: true, isValid: false };
3552
+ }
3553
+ __name(normalizeDisableValue, "normalizeDisableValue");
3554
+ var V2ToV3Migration = class {
3555
+ static {
3556
+ __name(this, "V2ToV3Migration");
3557
+ }
3558
+ fromVersion = 2;
3559
+ toVersion = 3;
3560
+ /**
3561
+ * Migration trigger rule.
3562
+ *
3563
+ * Execute only when `$version === 2`.
3564
+ * This includes V2 files with no migratable disable* booleans so that version
3565
+ * metadata still advances to 3.
3566
+ */
3567
+ shouldMigrate(settings) {
3568
+ if (typeof settings !== "object" || settings === null) {
3569
+ return false;
3570
+ }
3571
+ const s = settings;
3572
+ return s["$version"] === 2;
3573
+ }
3574
+ /**
3575
+ * Applies V2 -> V3 transformation with deterministic deprecated-key cleanup.
3576
+ *
3577
+ * Detailed strategy:
3578
+ * 1) Clone input.
3579
+ * 2) Process consolidated paths first:
3580
+ * - Inspect each source path.
3581
+ * - Normalize each present value (boolean / coercible string / invalid).
3582
+ * - Always delete present deprecated source key.
3583
+ * - Valid normalized values contribute to aggregate.
3584
+ * - Invalid values emit warnings.
3585
+ * - Emit consolidated target when at least one valid source was consumed.
3586
+ * 3) Process remaining one-to-one mappings:
3587
+ * - For each unmapped source, normalize value.
3588
+ * - If valid -> delete old key and write inverted target.
3589
+ * - If invalid -> delete old key and emit warning.
3590
+ * 4) Set `$version = 3`.
3591
+ *
3592
+ * Guarantees:
3593
+ * - Input object is not mutated.
3594
+ * - Valid migration and invalid cleanup are deterministic.
3595
+ * - Deprecated disable* keys are not retained after migration.
3596
+ */
3597
+ migrate(settings, scope) {
3598
+ if (typeof settings !== "object" || settings === null) {
3599
+ throw new Error("Settings must be an object");
3600
+ }
3601
+ const result = structuredClone(settings);
3602
+ const processedPaths = /* @__PURE__ */ new Set();
3603
+ const warnings = [];
3604
+ for (const [newPath, oldPaths] of Object.entries(CONSOLIDATED_V2_PATHS)) {
3605
+ let hasAnyDisable = false;
3606
+ let hasAnyBooleanValue = false;
3607
+ for (const oldPath of oldPaths) {
3608
+ const oldValue = getNestedProperty(result, oldPath);
3609
+ const normalized = normalizeDisableValue(oldValue);
3610
+ if (!normalized.isPresent) {
3611
+ continue;
3612
+ }
3613
+ deleteNestedPropertySafe(result, oldPath);
3614
+ processedPaths.add(oldPath);
3615
+ if (normalized.isValid) {
3616
+ hasAnyBooleanValue = true;
3617
+ if (normalized.booleanValue === true) {
3618
+ hasAnyDisable = true;
3619
+ }
3620
+ } else {
3621
+ warnings.push(
3622
+ `Removed deprecated setting '${oldPath}' from ${scope} settings because the value is invalid. Expected boolean.`
3623
+ );
3624
+ }
3625
+ }
3626
+ if (hasAnyBooleanValue) {
3627
+ setNestedPropertySafe(result, newPath, !hasAnyDisable);
3628
+ }
3629
+ }
3630
+ for (const [oldPath, newPath] of Object.entries(V2_TO_V3_BOOLEAN_MAP)) {
3631
+ if (processedPaths.has(oldPath)) {
3632
+ continue;
3633
+ }
3634
+ const oldValue = getNestedProperty(result, oldPath);
3635
+ const normalized = normalizeDisableValue(oldValue);
3636
+ if (!normalized.isPresent) {
3637
+ continue;
3638
+ }
3639
+ deleteNestedPropertySafe(result, oldPath);
3640
+ if (normalized.isValid) {
3641
+ setNestedPropertySafe(result, newPath, !normalized.booleanValue);
3642
+ } else {
3643
+ warnings.push(
3644
+ `Removed deprecated setting '${oldPath}' from ${scope} settings because the value is invalid. Expected boolean or string "true"/"false".`
3645
+ );
3646
+ }
3647
+ }
3648
+ result["$version"] = 3;
3649
+ return { settings: result, warnings };
3650
+ }
3651
+ };
3652
+ var v2ToV3Migration = new V2ToV3Migration();
3653
+
3654
+ // packages/cli/src/config/migration/versions/v3-to-v4.ts
3655
+ init_esbuild_shims();
3656
+ var GIT_CO_AUTHOR_PATH = "general.gitCoAuthor";
3657
+ var V3ToV4Migration = class {
3658
+ static {
3659
+ __name(this, "V3ToV4Migration");
3660
+ }
3661
+ fromVersion = 3;
3662
+ toVersion = 4;
3663
+ shouldMigrate(settings) {
3664
+ if (typeof settings !== "object" || settings === null) {
3665
+ return false;
3666
+ }
3667
+ const s = settings;
3668
+ if (s["$version"] === 3) {
3669
+ return true;
3670
+ }
3671
+ if (s["$version"] === void 0) {
3672
+ const value = getNestedProperty(s, GIT_CO_AUTHOR_PATH);
3673
+ if (value === void 0) return false;
3674
+ if (typeof value === "object" && value !== null && !Array.isArray(value)) {
3675
+ return false;
3676
+ }
3677
+ return true;
3678
+ }
3679
+ return false;
3680
+ }
3681
+ migrate(settings, scope) {
3682
+ if (typeof settings !== "object" || settings === null) {
3683
+ throw new Error("Settings must be an object");
3684
+ }
3685
+ const result = structuredClone(settings);
3686
+ const warnings = [];
3687
+ const value = getNestedProperty(result, GIT_CO_AUTHOR_PATH);
3688
+ if (typeof value === "boolean") {
3689
+ setNestedPropertySafe(result, GIT_CO_AUTHOR_PATH, {
3690
+ commit: value,
3691
+ pr: value
3692
+ });
3693
+ } else if (typeof value === "string") {
3694
+ const lowered = value.trim().toLowerCase();
3695
+ const disableIntent = ["false", "no", "off", "0", "disabled", ""];
3696
+ const enableIntent = ["true", "yes", "on", "1", "enabled"];
3697
+ if (enableIntent.includes(lowered)) {
3698
+ setNestedPropertySafe(result, GIT_CO_AUTHOR_PATH, {
3699
+ commit: true,
3700
+ pr: true
3701
+ });
3702
+ } else if (disableIntent.includes(lowered)) {
3703
+ setNestedPropertySafe(result, GIT_CO_AUTHOR_PATH, {
3704
+ commit: false,
3705
+ pr: false
3706
+ });
3707
+ } else {
3708
+ setNestedPropertySafe(result, GIT_CO_AUTHOR_PATH, {
3709
+ commit: false,
3710
+ pr: false
3711
+ });
3712
+ warnings.push(
3713
+ `Reset '${GIT_CO_AUTHOR_PATH}' in ${scope} settings to {commit: false, pr: false} because the stored string '${value}' was not a recognized boolean form.`
3714
+ );
3715
+ }
3716
+ } else if (value !== void 0 && (typeof value !== "object" || value === null || Array.isArray(value))) {
3717
+ setNestedPropertySafe(result, GIT_CO_AUTHOR_PATH, {
3718
+ commit: false,
3719
+ pr: false
3720
+ });
3721
+ warnings.push(
3722
+ `Reset '${GIT_CO_AUTHOR_PATH}' in ${scope} settings to {commit: false, pr: false} because the stored value was not a boolean or object.`
3723
+ );
3724
+ }
3725
+ result["$version"] = 4;
3726
+ return { settings: result, warnings };
3727
+ }
3728
+ };
3729
+ var v3ToV4Migration = new V3ToV4Migration();
3730
+
3731
+ // packages/cli/src/config/migration/versions/v5-to-v4.ts
3732
+ init_esbuild_shims();
3733
+ var PROVIDER_KEY_TO_PROTOCOL = {
3734
+ openai: "openai",
3735
+ "qwen-oauth": "qwen-oauth",
3736
+ gemini: "gemini",
3737
+ "vertex-ai": "gemini",
3738
+ anthropic: "anthropic"
3739
+ };
3740
+ function isWrappedProviderConfig(value) {
3741
+ return typeof value === "object" && value !== null && !Array.isArray(value);
3742
+ }
3743
+ __name(isWrappedProviderConfig, "isWrappedProviderConfig");
3744
+ var V5ToV4Migration = class {
3745
+ static {
3746
+ __name(this, "V5ToV4Migration");
3747
+ }
3748
+ fromVersion = 5;
3749
+ toVersion = 4;
3750
+ shouldMigrate(settings) {
3751
+ if (typeof settings !== "object" || settings === null) {
3752
+ return false;
3753
+ }
3754
+ const s = settings;
3755
+ if (s["$version"] === 5) {
3756
+ return true;
3757
+ }
3758
+ if (s["$version"] !== void 0) {
3759
+ return false;
3760
+ }
3761
+ const modelProviders = s["modelProviders"];
3762
+ if (typeof modelProviders !== "object" || modelProviders === null) {
3763
+ return false;
3764
+ }
3765
+ return Object.values(modelProviders).some(
3766
+ (v) => isWrappedProviderConfig(v)
3767
+ );
3768
+ }
3769
+ migrate(settings, _scope) {
3770
+ if (typeof settings !== "object" || settings === null) {
3771
+ throw new Error("Settings must be an object");
3772
+ }
3773
+ const result = structuredClone(settings);
3774
+ const warnings = [];
3775
+ const modelProviders = result["modelProviders"];
3776
+ if (typeof modelProviders === "object" && modelProviders !== null) {
3777
+ const providers = modelProviders;
3778
+ for (const [key, value] of Object.entries(providers)) {
3779
+ if (Array.isArray(value)) {
3780
+ continue;
3781
+ }
3782
+ if (!isWrappedProviderConfig(value)) {
3783
+ continue;
3784
+ }
3785
+ const derivedProtocol = Object.hasOwn(PROVIDER_KEY_TO_PROTOCOL, key) ? PROVIDER_KEY_TO_PROTOCOL[key] : void 0;
3786
+ if (typeof value.protocol === "string" && derivedProtocol !== void 0 && value.protocol !== derivedProtocol) {
3787
+ warnings.push(
3788
+ `Provider "${key}" declared protocol "${value.protocol}", but V4 derives protocol "${derivedProtocol}" from the provider key. The explicit protocol has been dropped.`
3789
+ );
3790
+ }
3791
+ providers[key] = Array.isArray(value.models) ? value.models : [];
3792
+ }
3793
+ }
3794
+ result["$version"] = 4;
3795
+ return { settings: result, warnings };
3796
+ }
3797
+ };
3798
+ var v5ToV4Migration = new V5ToV4Migration();
3799
+
3800
+ // packages/cli/src/config/migration/index.ts
3801
+ var ALL_MIGRATIONS = [
3802
+ v1ToV2Migration,
3803
+ v2ToV3Migration,
3804
+ v3ToV4Migration
3805
+ ];
3806
+ var DOWNGRADE_MIGRATIONS = [v5ToV4Migration];
3807
+ var CONVERGENCE_MIGRATIONS = [
3808
+ ...ALL_MIGRATIONS,
3809
+ ...DOWNGRADE_MIGRATIONS
3810
+ ];
3811
+ function runMigrations(settings, scope) {
3812
+ const scheduler = new MigrationScheduler([...CONVERGENCE_MIGRATIONS], scope);
3813
+ return scheduler.migrate(settings);
3814
+ }
3815
+ __name(runMigrations, "runMigrations");
3816
+ function needsMigration(settings) {
3817
+ if (typeof settings !== "object" || settings === null) {
3818
+ return false;
3819
+ }
3820
+ const s = settings;
3821
+ const version = s["$version"];
3822
+ const hasApplicableMigration = CONVERGENCE_MIGRATIONS.some(
3823
+ (migration) => migration.shouldMigrate(settings)
3824
+ );
3825
+ if (typeof version === "number") {
3826
+ if (version === SETTINGS_VERSION) {
3827
+ return false;
3828
+ }
3829
+ return hasApplicableMigration;
3830
+ }
3831
+ return hasApplicableMigration;
3832
+ }
3833
+ __name(needsMigration, "needsMigration");
3834
+
3835
+ // packages/cli/src/config/environment.ts
3836
+ init_esbuild_shims();
3837
+ var dotenv = __toESM(require_main(), 1);
3838
+ import * as fs3 from "node:fs";
3839
+ import * as path2 from "node:path";
3840
+ import * as os from "node:os";
3841
+ var SETTINGS_DIRECTORY_NAME = QWEN_DIR;
3842
+ var RELOAD_EXCLUDED_KEYS = /* @__PURE__ */ new Set([
3843
+ ...PROJECT_ENV_HARDCODED_EXCLUSIONS,
3844
+ "QWEN_SERVER_TOKEN",
3845
+ "QWEN_CLI_ENTRY",
3846
+ "NODE_OPTIONS",
3847
+ "NODE_PATH",
3848
+ "NODE_TLS_REJECT_UNAUTHORIZED",
3849
+ "LD_PRELOAD",
3850
+ "LD_AUDIT",
3851
+ "LD_LIBRARY_PATH",
3852
+ "DYLD_INSERT_LIBRARIES",
3853
+ "DYLD_LIBRARY_PATH",
3854
+ "BASH_ENV",
3855
+ "ENV",
3856
+ "PATH",
3857
+ "HOME",
3858
+ "TMPDIR",
3859
+ "TMP",
3860
+ "TEMP"
3861
+ ]);
3862
+ var dotEnvSourcedKeys = /* @__PURE__ */ new Set();
3863
+ var settingsEnvSourcedKeys = /* @__PURE__ */ new Set();
3864
+ var lastReloadSnapshot = /* @__PURE__ */ new Map();
3865
+ var lastReloadSnapshotSeeded = false;
3866
+ function getUserLevelEnvPaths() {
3867
+ const homeDir = os.homedir();
3868
+ const globalQwenDir = Storage.getGlobalQwenDir();
3869
+ const paths = /* @__PURE__ */ new Set([
3870
+ path2.normalize(path2.join(homeDir, ".env")),
3871
+ path2.normalize(path2.join(globalQwenDir, ".env"))
3872
+ ]);
3873
+ const legacyQwenEnv = path2.normalize(path2.join(homeDir, QWEN_DIR, ".env"));
3874
+ paths.add(legacyQwenEnv);
3875
+ return paths;
3876
+ }
3877
+ __name(getUserLevelEnvPaths, "getUserLevelEnvPaths");
3878
+ var homeEnvBootstrapped = false;
3879
+ function preResolveHomeEnvOverrides() {
3880
+ if (homeEnvBootstrapped) {
3881
+ return;
3882
+ }
3883
+ homeEnvBootstrapped = true;
3884
+ if (HOME_ENV_BOOTSTRAP_KEYS.every((key) => process.env[key])) {
3885
+ return;
3886
+ }
3887
+ const initialQwenHome = process.env["QWEN_HOME"];
3888
+ const initialQwenDir = Storage.getGlobalQwenDir();
3889
+ const candidates = [path2.join(initialQwenDir, ".env")];
3890
+ if (!initialQwenHome) {
3891
+ candidates.push(path2.join(path2.dirname(initialQwenDir), ".env"));
3892
+ }
3893
+ for (const candidate of candidates) {
3894
+ readHomeEnvInto(candidate);
3895
+ }
3896
+ const discoveredQwenHome = process.env["QWEN_HOME"];
3897
+ if (discoveredQwenHome && discoveredQwenHome !== initialQwenHome) {
3898
+ const discoveredDir = Storage.getGlobalQwenDir();
3899
+ if (discoveredDir !== initialQwenDir) {
3900
+ readHomeEnvInto(path2.join(discoveredDir, ".env"));
3901
+ }
3902
+ }
3903
+ }
3904
+ __name(preResolveHomeEnvOverrides, "preResolveHomeEnvOverrides");
3905
+ function readHomeEnvInto(file) {
3906
+ if (!fs3.existsSync(file)) {
3907
+ return;
3908
+ }
3909
+ try {
3910
+ const parsed = dotenv.parse(fs3.readFileSync(file, "utf-8"));
3911
+ for (const key of PROJECT_ENV_HARDCODED_EXCLUSIONS) {
3912
+ if (parsed[key] && !Object.hasOwn(process.env, key)) {
3913
+ process.env[key] = parsed[key];
3914
+ }
3915
+ }
3916
+ } catch (_e) {
3917
+ }
3918
+ }
3919
+ __name(readHomeEnvInto, "readHomeEnvInto");
3920
+ function resetHomeEnvBootstrapForTesting() {
3921
+ homeEnvBootstrapped = false;
3922
+ }
3923
+ __name(resetHomeEnvBootstrapForTesting, "resetHomeEnvBootstrapForTesting");
3924
+ function resetEnvironmentTrackingForTesting() {
3925
+ dotEnvSourcedKeys.clear();
3926
+ settingsEnvSourcedKeys.clear();
3927
+ lastReloadSnapshot.clear();
3928
+ lastReloadSnapshotSeeded = false;
3929
+ }
3930
+ __name(resetEnvironmentTrackingForTesting, "resetEnvironmentTrackingForTesting");
3931
+ function getHomeEnvFallbackVars(onReadError) {
3932
+ const globalQwenDir = Storage.getGlobalQwenDir();
3933
+ const candidates = [path2.join(globalQwenDir, ".env")];
3934
+ if (!process.env["QWEN_HOME"]) {
3935
+ candidates.push(path2.join(path2.dirname(globalQwenDir), ".env"));
3936
+ }
3937
+ const result = {};
3938
+ for (const candidate of candidates) {
3939
+ if (!fs3.existsSync(candidate)) {
3940
+ continue;
3941
+ }
3942
+ try {
3943
+ const parsed = dotenv.parse(fs3.readFileSync(candidate, "utf-8"));
3944
+ for (const key in parsed) {
3945
+ if (Object.hasOwn(parsed, key) && !Object.hasOwn(process.env, key)) {
3946
+ result[key] ??= parsed[key];
3947
+ }
3948
+ }
3949
+ } catch (e) {
3950
+ onReadError?.(
3951
+ `Failed to read home .env candidate ${candidate}: ${getErrorMessage(e)}`
3952
+ );
3953
+ }
3954
+ }
3955
+ return result;
3956
+ }
3957
+ __name(getHomeEnvFallbackVars, "getHomeEnvFallbackVars");
3958
+ function findEnvFiles(settings, startDir, userLevelPaths = getUserLevelEnvPaths()) {
3959
+ const homeDir = os.homedir();
3960
+ let realStartDir = path2.resolve(startDir);
3961
+ try {
3962
+ realStartDir = fs3.realpathSync(realStartDir);
3963
+ } catch {
3964
+ }
3965
+ const isTrusted = isWorkspaceTrusted(
3966
+ settings,
3967
+ void 0,
3968
+ realStartDir
3969
+ ).isTrusted;
3970
+ const globalQwenDir = Storage.getGlobalQwenDir();
3971
+ const legacyQwenDir = path2.normalize(path2.join(homeDir, QWEN_DIR));
3972
+ const hasCustomConfigDir = path2.normalize(globalQwenDir) !== legacyQwenDir;
3973
+ const found = [];
3974
+ const seen = /* @__PURE__ */ new Set();
3975
+ const canUseEnvFile = /* @__PURE__ */ __name((filePath) => isTrusted !== false || userLevelPaths.has(path2.normalize(filePath)), "canUseEnvFile");
3976
+ const pushCandidate = /* @__PURE__ */ __name((filePath) => {
3977
+ const normalized = path2.normalize(filePath);
3978
+ if (!seen.has(normalized) && fs3.existsSync(filePath) && canUseEnvFile(filePath)) {
3979
+ seen.add(normalized);
3980
+ found.push(filePath);
3981
+ return true;
3982
+ }
3983
+ return false;
3984
+ }, "pushCandidate");
3985
+ const pushHomeCandidates = /* @__PURE__ */ __name(() => {
3986
+ const candidates = [path2.join(globalQwenDir, ".env")];
3987
+ if (hasCustomConfigDir) {
3988
+ candidates.push(path2.join(legacyQwenDir, ".env"));
3989
+ }
3990
+ candidates.push(path2.join(homeDir, ".env"));
3991
+ for (const candidate of candidates) {
3992
+ pushCandidate(candidate);
3993
+ }
3994
+ }, "pushHomeCandidates");
3995
+ let currentDir = realStartDir;
3996
+ let visitedHomeDir = false;
3997
+ while (true) {
3998
+ if (currentDir === homeDir) {
3999
+ visitedHomeDir = true;
4000
+ pushHomeCandidates();
4001
+ return found;
4002
+ } else {
4003
+ const geminiEnvPath = path2.join(currentDir, QWEN_DIR, ".env");
4004
+ if (pushCandidate(geminiEnvPath)) {
4005
+ pushHomeCandidates();
4006
+ return found;
4007
+ }
4008
+ const envPath = path2.join(currentDir, ".env");
4009
+ if (pushCandidate(envPath)) {
4010
+ pushHomeCandidates();
4011
+ return found;
4012
+ }
4013
+ }
4014
+ const parentDir = path2.dirname(currentDir);
4015
+ if (parentDir === currentDir || !parentDir) {
4016
+ if (!visitedHomeDir) {
4017
+ pushHomeCandidates();
4018
+ }
4019
+ return found;
4020
+ }
4021
+ currentDir = parentDir;
4022
+ }
4023
+ }
4024
+ __name(findEnvFiles, "findEnvFiles");
4025
+ function setUpCloudShellEnvironment(envFilePath) {
4026
+ if (envFilePath && fs3.existsSync(envFilePath)) {
4027
+ const envFileContent = fs3.readFileSync(envFilePath);
4028
+ const parsedEnv = dotenv.parse(envFileContent);
4029
+ if (parsedEnv["GOOGLE_CLOUD_PROJECT"]) {
4030
+ process.env["GOOGLE_CLOUD_PROJECT"] = parsedEnv["GOOGLE_CLOUD_PROJECT"];
4031
+ } else {
4032
+ process.env["GOOGLE_CLOUD_PROJECT"] = "cloudshell-gca";
4033
+ }
4034
+ } else {
4035
+ process.env["GOOGLE_CLOUD_PROJECT"] = "cloudshell-gca";
4036
+ }
4037
+ }
4038
+ __name(setUpCloudShellEnvironment, "setUpCloudShellEnvironment");
4039
+ function setUpCloudShellEnvironmentFromFiles(envFilePaths) {
4040
+ for (const envFilePath of envFilePaths) {
4041
+ if (!fs3.existsSync(envFilePath)) {
4042
+ continue;
4043
+ }
4044
+ const envFileContent = fs3.readFileSync(envFilePath);
4045
+ const parsedEnv = dotenv.parse(envFileContent);
4046
+ if (parsedEnv["GOOGLE_CLOUD_PROJECT"]) {
4047
+ process.env["GOOGLE_CLOUD_PROJECT"] = parsedEnv["GOOGLE_CLOUD_PROJECT"];
4048
+ return;
4049
+ }
4050
+ }
4051
+ process.env["GOOGLE_CLOUD_PROJECT"] = "cloudshell-gca";
4052
+ }
4053
+ __name(setUpCloudShellEnvironmentFromFiles, "setUpCloudShellEnvironmentFromFiles");
4054
+ function loadEnvironment(settings, startDir = process.cwd()) {
4055
+ const userLevelPaths = getUserLevelEnvPaths();
4056
+ const envFilePaths = findEnvFiles(settings, startDir, userLevelPaths);
4057
+ if (process.env["CLOUD_SHELL"] === "true") {
4058
+ setUpCloudShellEnvironmentFromFiles(envFilePaths);
4059
+ }
4060
+ for (const envFilePath of envFilePaths) {
4061
+ try {
4062
+ const envFileContent = fs3.readFileSync(envFilePath, "utf-8");
4063
+ const parsedEnv = dotenv.parse(envFileContent);
4064
+ const excludedVars = settings?.advanced?.excludedEnvVars || DEFAULT_EXCLUDED_ENV_VARS;
4065
+ const normalizedEnvFilePath = path2.normalize(envFilePath);
4066
+ const isHomeScopedEnvFile = userLevelPaths.has(normalizedEnvFilePath);
4067
+ const isQwenScopedEnvFile = isHomeScopedEnvFile || path2.basename(path2.dirname(normalizedEnvFilePath)) === QWEN_DIR;
4068
+ for (const key in parsedEnv) {
4069
+ if (Object.hasOwn(parsedEnv, key)) {
4070
+ if (!isHomeScopedEnvFile && PROJECT_ENV_HARDCODED_EXCLUSIONS.includes(key)) {
4071
+ continue;
4072
+ }
4073
+ if (!isQwenScopedEnvFile && excludedVars.includes(key)) {
4074
+ continue;
4075
+ }
4076
+ if (!Object.hasOwn(process.env, key)) {
4077
+ process.env[key] = parsedEnv[key];
4078
+ dotEnvSourcedKeys.add(key);
4079
+ }
4080
+ if (!lastReloadSnapshotSeeded && !lastReloadSnapshot.has(key)) {
4081
+ lastReloadSnapshot.set(key, parsedEnv[key]);
4082
+ }
4083
+ }
4084
+ }
4085
+ } catch (_e) {
4086
+ }
4087
+ }
4088
+ if (settings.env) {
4089
+ for (const [key, value] of Object.entries(settings.env)) {
4090
+ if (PROJECT_ENV_HARDCODED_EXCLUSIONS.includes(key)) {
4091
+ continue;
4092
+ }
4093
+ if (!Object.hasOwn(process.env, key) && typeof value === "string") {
4094
+ process.env[key] = value;
4095
+ settingsEnvSourcedKeys.add(key);
4096
+ }
4097
+ if (!lastReloadSnapshotSeeded && typeof value === "string" && !lastReloadSnapshot.has(key)) {
4098
+ lastReloadSnapshot.set(key, value);
4099
+ }
4100
+ }
4101
+ }
4102
+ lastReloadSnapshotSeeded = true;
4103
+ }
4104
+ __name(loadEnvironment, "loadEnvironment");
4105
+ function reloadEnvironment(settings, workspaceCwd) {
4106
+ const userLevelPaths = getUserLevelEnvPaths();
4107
+ const envFilePaths = findEnvFiles(settings, workspaceCwd, userLevelPaths);
4108
+ if (process.env["CLOUD_SHELL"] === "true") {
4109
+ setUpCloudShellEnvironmentFromFiles(envFilePaths);
4110
+ }
4111
+ let dotEnvReadFailed = false;
4112
+ const newDotEnvKeys = /* @__PURE__ */ new Map();
4113
+ const newSettingsEnvKeys = /* @__PURE__ */ new Map();
4114
+ for (const envFilePath of envFilePaths) {
4115
+ try {
4116
+ const envFileContent = fs3.readFileSync(envFilePath, "utf-8");
4117
+ const parsedEnv = dotenv.parse(envFileContent);
4118
+ const excludedVars = settings?.advanced?.excludedEnvVars || DEFAULT_EXCLUDED_ENV_VARS;
4119
+ const normalizedEnvFilePath = path2.normalize(envFilePath);
4120
+ const isHomeScopedEnvFile = userLevelPaths.has(normalizedEnvFilePath);
4121
+ const isQwenScopedEnvFile = isHomeScopedEnvFile || path2.basename(path2.dirname(normalizedEnvFilePath)) === QWEN_DIR;
4122
+ for (const key in parsedEnv) {
4123
+ if (!Object.hasOwn(parsedEnv, key)) continue;
4124
+ if (RELOAD_EXCLUDED_KEYS.has(key)) continue;
4125
+ if (!isHomeScopedEnvFile && PROJECT_ENV_HARDCODED_EXCLUSIONS.includes(key)) {
4126
+ continue;
4127
+ }
4128
+ if (!isQwenScopedEnvFile && excludedVars.includes(key)) continue;
4129
+ if (!newDotEnvKeys.has(key)) {
4130
+ newDotEnvKeys.set(key, parsedEnv[key]);
4131
+ }
4132
+ }
4133
+ } catch {
4134
+ dotEnvReadFailed = true;
4135
+ }
4136
+ }
4137
+ if (settings.env) {
4138
+ for (const [key, value] of Object.entries(settings.env)) {
4139
+ if (RELOAD_EXCLUDED_KEYS.has(key)) continue;
4140
+ if (PROJECT_ENV_HARDCODED_EXCLUSIONS.includes(key)) continue;
4141
+ if (typeof value !== "string") continue;
4142
+ if (newDotEnvKeys.has(key)) continue;
4143
+ if (dotEnvReadFailed && lastReloadSnapshot.has(key)) continue;
4144
+ newSettingsEnvKeys.set(key, value);
4145
+ }
4146
+ }
4147
+ const allNewKeys = /* @__PURE__ */ new Set([
4148
+ ...newDotEnvKeys.keys(),
4149
+ ...newSettingsEnvKeys.keys()
4150
+ ]);
4151
+ const updatedKeys = [];
4152
+ const removedKeys = [];
4153
+ if (!dotEnvReadFailed) {
4154
+ const previouslyKnown = /* @__PURE__ */ new Set([
4155
+ ...lastReloadSnapshot.keys(),
4156
+ ...dotEnvSourcedKeys,
4157
+ ...settingsEnvSourcedKeys
4158
+ ]);
4159
+ for (const key of previouslyKnown) {
4160
+ if (!allNewKeys.has(key) && !RELOAD_EXCLUDED_KEYS.has(key)) {
4161
+ delete process.env[key];
4162
+ removedKeys.push(key);
4163
+ }
4164
+ }
4165
+ }
4166
+ for (const [key, value] of newDotEnvKeys) {
4167
+ if (process.env[key] !== value) {
4168
+ updatedKeys.push(key);
4169
+ }
4170
+ process.env[key] = value;
4171
+ }
4172
+ for (const [key, value] of newSettingsEnvKeys) {
4173
+ if (process.env[key] !== value) {
4174
+ updatedKeys.push(key);
4175
+ }
4176
+ process.env[key] = value;
4177
+ }
4178
+ if (!dotEnvReadFailed) {
4179
+ dotEnvSourcedKeys.clear();
4180
+ for (const key of newDotEnvKeys.keys()) {
4181
+ dotEnvSourcedKeys.add(key);
4182
+ }
4183
+ lastReloadSnapshot.clear();
4184
+ for (const [key, value] of newDotEnvKeys) {
4185
+ lastReloadSnapshot.set(key, value);
4186
+ }
4187
+ for (const [key, value] of newSettingsEnvKeys) {
4188
+ lastReloadSnapshot.set(key, value);
4189
+ }
4190
+ }
4191
+ settingsEnvSourcedKeys.clear();
4192
+ for (const key of newSettingsEnvKeys.keys()) {
4193
+ settingsEnvSourcedKeys.add(key);
4194
+ }
4195
+ return { updatedKeys, removedKeys };
4196
+ }
4197
+ __name(reloadEnvironment, "reloadEnvironment");
4198
+
4199
+ // packages/cli/src/config/settings.ts
4200
+ var debugLogger2 = createDebugLogger("SETTINGS");
4201
+ function getMergeStrategyForPath(path4) {
4202
+ let current = void 0;
4203
+ let currentSchema = getSettingsSchema();
4204
+ for (const key of path4) {
4205
+ if (!currentSchema || !currentSchema[key]) {
4206
+ return void 0;
4207
+ }
4208
+ current = currentSchema[key];
4209
+ currentSchema = current.properties;
4210
+ }
4211
+ return current?.mergeStrategy;
4212
+ }
4213
+ __name(getMergeStrategyForPath, "getMergeStrategyForPath");
4214
+ function getUserSettingsPath() {
4215
+ return Storage.getGlobalSettingsPath();
4216
+ }
4217
+ __name(getUserSettingsPath, "getUserSettingsPath");
4218
+ function getUserSettingsDir() {
4219
+ return path3.dirname(getUserSettingsPath());
4220
+ }
4221
+ __name(getUserSettingsDir, "getUserSettingsDir");
4222
+ var SETTINGS_VERSION = 4;
4223
+ var SETTINGS_VERSION_KEY = "$version";
4224
+ function migrateLegacyPermissions(settings) {
4225
+ const tools = settings["tools"];
4226
+ if (!tools) return null;
4227
+ const hasLegacy = Array.isArray(tools["core"]) || Array.isArray(tools["allowed"]) || Array.isArray(tools["exclude"]);
4228
+ if (!hasLegacy) return null;
4229
+ const result = structuredClone(settings);
4230
+ const resultTools = result["tools"];
4231
+ const permissions = result["permissions"] ?? {};
4232
+ result["permissions"] = permissions;
4233
+ const mergeInto = /* @__PURE__ */ __name((key, items) => {
4234
+ const existing = Array.isArray(permissions[key]) ? permissions[key] : [];
4235
+ const merged = Array.from(/* @__PURE__ */ new Set([...existing, ...items]));
4236
+ permissions[key] = merged;
4237
+ }, "mergeInto");
4238
+ if (Array.isArray(resultTools["allowed"])) {
4239
+ mergeInto("allow", resultTools["allowed"]);
4240
+ delete resultTools["allowed"];
4241
+ }
4242
+ if (Array.isArray(resultTools["exclude"])) {
4243
+ mergeInto("deny", resultTools["exclude"]);
4244
+ delete resultTools["exclude"];
4245
+ }
4246
+ if (Array.isArray(resultTools["core"])) {
4247
+ mergeInto("allow", resultTools["core"]);
4248
+ delete resultTools["core"];
4249
+ }
4250
+ return result;
4251
+ }
4252
+ __name(migrateLegacyPermissions, "migrateLegacyPermissions");
4253
+ var SettingScope = /* @__PURE__ */ ((SettingScope2) => {
4254
+ SettingScope2["User"] = "User";
4255
+ SettingScope2["Workspace"] = "Workspace";
4256
+ SettingScope2["System"] = "System";
4257
+ SettingScope2["SystemDefaults"] = "SystemDefaults";
4258
+ return SettingScope2;
4259
+ })(SettingScope || {});
4260
+ function getSettingsFileKeyWarnings(settings, settingsFilePath) {
4261
+ const version = settings[SETTINGS_VERSION_KEY];
4262
+ if (typeof version !== "number" || version < SETTINGS_VERSION) {
4263
+ return [];
4264
+ }
4265
+ const warnings = [];
4266
+ const ignoredLegacyKeys = /* @__PURE__ */ new Set();
4267
+ for (const [oldKey, newPath] of Object.entries(V1_TO_V2_MIGRATION_MAP)) {
4268
+ if (oldKey === newPath) {
4269
+ continue;
4270
+ }
4271
+ if (!(oldKey in settings)) {
4272
+ continue;
4273
+ }
4274
+ const oldValue = settings[oldKey];
4275
+ if (V2_CONTAINER_KEYS.has(oldKey) && typeof oldValue === "object" && oldValue !== null && !Array.isArray(oldValue)) {
4276
+ continue;
4277
+ }
4278
+ ignoredLegacyKeys.add(oldKey);
4279
+ warnings.push(
4280
+ `Warning: Legacy setting '${oldKey}' will be ignored in ${settingsFilePath}. Please use '${newPath}' instead.`
4281
+ );
4282
+ }
4283
+ const schemaKeys = new Set(Object.keys(getSettingsSchema()));
4284
+ for (const key of Object.keys(settings)) {
4285
+ if (key === SETTINGS_VERSION_KEY) {
4286
+ continue;
4287
+ }
4288
+ if (ignoredLegacyKeys.has(key)) {
4289
+ continue;
4290
+ }
4291
+ if (schemaKeys.has(key)) {
4292
+ continue;
4293
+ }
4294
+ debugLogger2.warn(
4295
+ `Unknown setting '${key}' will be ignored in ${settingsFilePath}.`
4296
+ );
4297
+ }
4298
+ return warnings;
4299
+ }
4300
+ __name(getSettingsFileKeyWarnings, "getSettingsFileKeyWarnings");
4301
+ function isPlainObject2(value) {
4302
+ return typeof value === "object" && value !== null && !Array.isArray(value);
4303
+ }
4304
+ __name(isPlainObject2, "isPlainObject");
4305
+ function hasAnyProviderEntries(modelProviders) {
4306
+ if (!isPlainObject2(modelProviders)) {
4307
+ return false;
4308
+ }
4309
+ return Object.values(modelProviders).some(
4310
+ (providerModels) => Array.isArray(providerModels) && providerModels.length > 0
4311
+ );
4312
+ }
4313
+ __name(hasAnyProviderEntries, "hasAnyProviderEntries");
4314
+ function getModelProvidersOverrideWarnings(loadedSettings) {
4315
+ if (!loadedSettings.isTrusted) {
4316
+ return [];
4317
+ }
4318
+ const userOriginal = loadedSettings.user.originalSettings;
4319
+ const workspaceOriginal = loadedSettings.workspace.originalSettings;
4320
+ if (!hasOwnModelProviders(userOriginal) || !hasOwnModelProviders(workspaceOriginal)) {
4321
+ return [];
4322
+ }
4323
+ const userModelProviders = userOriginal["modelProviders"];
4324
+ const workspaceModelProviders = workspaceOriginal["modelProviders"];
4325
+ const workspaceIsEmptyModelProviders = isPlainObject2(workspaceModelProviders) && Object.keys(workspaceModelProviders).length === 0;
4326
+ if (!workspaceIsEmptyModelProviders || !hasAnyProviderEntries(userModelProviders)) {
4327
+ return [];
4328
+ }
4329
+ return [
4330
+ `Warning: '${loadedSettings.workspace.path}' defines an empty 'modelProviders' object. This has no effect with current merge behavior, but may indicate a configuration error. If REPLACE semantics are introduced for 'modelProviders' in the future, this would override user-level model providers in '${loadedSettings.user.path}'.`
4331
+ ];
4332
+ }
4333
+ __name(getModelProvidersOverrideWarnings, "getModelProvidersOverrideWarnings");
4334
+ function getSettingsWarnings(loadedSettings) {
4335
+ const warningSet = /* @__PURE__ */ new Set();
4336
+ for (const warning of loadedSettings.migrationWarnings) {
4337
+ warningSet.add(`Warning: ${warning}`);
4338
+ }
4339
+ for (const scope of ["User" /* User */, "Workspace" /* Workspace */]) {
4340
+ const settingsFile = loadedSettings.forScope(scope);
4341
+ if (settingsFile.rawJson === void 0) {
4342
+ continue;
4343
+ }
4344
+ const settingsObject = settingsFile.originalSettings;
4345
+ for (const warning of getSettingsFileKeyWarnings(
4346
+ settingsObject,
4347
+ settingsFile.path
4348
+ )) {
4349
+ warningSet.add(warning);
4350
+ }
4351
+ }
4352
+ for (const warning of getModelProvidersOverrideWarnings(loadedSettings)) {
4353
+ warningSet.add(warning);
4354
+ }
4355
+ return [...warningSet];
4356
+ }
4357
+ __name(getSettingsWarnings, "getSettingsWarnings");
4358
+ function tagMcpServerScope(settings, scope) {
4359
+ const servers = settings.mcpServers;
4360
+ if (!servers || Object.keys(servers).length === 0) {
4361
+ return settings;
4362
+ }
4363
+ const tagged = {};
4364
+ for (const [name, config] of Object.entries(servers)) {
4365
+ tagged[name] = { ...config, scope };
4366
+ }
4367
+ return { ...settings, mcpServers: tagged };
4368
+ }
4369
+ __name(tagMcpServerScope, "tagMcpServerScope");
4370
+ function mergeSettings(system, systemDefaults, user, workspace, isTrusted) {
4371
+ const safeWorkspace = isTrusted ? tagMcpServerScope(workspace, "workspace") : {};
4372
+ return customDeepMerge(
4373
+ getMergeStrategyForPath,
4374
+ {},
4375
+ // Start with an empty object
4376
+ systemDefaults,
4377
+ user,
4378
+ safeWorkspace,
4379
+ tagMcpServerScope(system, "system")
4380
+ );
4381
+ }
4382
+ __name(mergeSettings, "mergeSettings");
4383
+ var LoadedSettings = class {
4384
+ static {
4385
+ __name(this, "LoadedSettings");
4386
+ }
4387
+ constructor(system, systemDefaults, user, workspace, isTrusted, migratedInMemorScopes, migrationWarnings = [], corruptedPath = void 0, wasRecovered = false, workspaceSettingsActive = true) {
4388
+ this.system = system;
4389
+ this.systemDefaults = systemDefaults;
4390
+ this.user = user;
4391
+ this.workspace = workspace;
4392
+ this.isTrusted = isTrusted;
4393
+ this.migratedInMemorScopes = migratedInMemorScopes;
4394
+ this.migrationWarnings = migrationWarnings;
4395
+ this.corruptedPath = corruptedPath;
4396
+ this.wasRecovered = wasRecovered;
4397
+ this.workspaceSettingsActive = workspaceSettingsActive;
4398
+ this._merged = this.computeMergedSettings();
4399
+ }
4400
+ system;
4401
+ systemDefaults;
4402
+ user;
4403
+ workspace;
4404
+ isTrusted;
4405
+ migratedInMemorScopes;
4406
+ migrationWarnings;
4407
+ corruptedPath;
4408
+ wasRecovered;
4409
+ workspaceSettingsActive;
4410
+ corruptionDialogDismissed = false;
4411
+ _merged;
4412
+ get merged() {
4413
+ return this._merged;
4414
+ }
4415
+ computeMergedSettings() {
4416
+ return mergeSettings(
4417
+ this.system.settings,
4418
+ this.systemDefaults.settings,
4419
+ this.user.settings,
4420
+ this.workspace.settings,
4421
+ this.isTrusted
4422
+ );
4423
+ }
4424
+ forScope(scope) {
4425
+ switch (scope) {
4426
+ case "User" /* User */:
4427
+ return this.user;
4428
+ case "Workspace" /* Workspace */:
4429
+ return this.workspace;
4430
+ case "System" /* System */:
4431
+ return this.system;
4432
+ case "SystemDefaults" /* SystemDefaults */:
4433
+ return this.systemDefaults;
4434
+ default:
4435
+ throw new Error(`Invalid scope: ${scope}`);
4436
+ }
4437
+ }
4438
+ setValue(scope, key, value) {
4439
+ if (key === "model.name" && typeof value === "string") {
4440
+ value = stripRuntimeSnapshotPrefix(value);
4441
+ }
4442
+ const settingsFile = this.forScope(scope);
4443
+ setNestedPropertySafe(settingsFile.settings, key, value);
4444
+ setNestedPropertySafe(settingsFile.originalSettings, key, value);
4445
+ this._merged = this.computeMergedSettings();
4446
+ const replacePath = key === "mcpServers" ? key.split(".") : [];
4447
+ saveSettings(settingsFile, createSettingsUpdate(key, value), replacePath);
4448
+ }
4449
+ setValues(writes, onScopeCommitted) {
4450
+ const scopes = /* @__PURE__ */ new Set();
4451
+ for (const write of writes) {
4452
+ const value = write.key === "model.name" && typeof write.value === "string" ? stripRuntimeSnapshotPrefix(write.value) : write.value;
4453
+ const settingsFile = this.forScope(write.scope);
4454
+ setNestedPropertySafe(settingsFile.settings, write.key, value);
4455
+ setNestedPropertySafe(settingsFile.originalSettings, write.key, value);
4456
+ scopes.add(write.scope);
4457
+ }
4458
+ this._merged = this.computeMergedSettings();
4459
+ const scopeList = Array.from(scopes);
4460
+ for (let i = 0; i < scopeList.length; i++) {
4461
+ const scope = scopeList[i];
4462
+ try {
4463
+ saveSettings(this.forScope(scope), void 0, void 0, {
4464
+ throwOnWriteFailure: true
4465
+ });
4466
+ } catch (err) {
4467
+ for (const uncommittedScope of scopeList.slice(i)) {
4468
+ this.reloadScopeFromDisk(uncommittedScope);
4469
+ }
4470
+ throw err;
4471
+ }
4472
+ onScopeCommitted?.(scope);
4473
+ }
4474
+ }
4475
+ recomputeMerged() {
4476
+ this._merged = this.computeMergedSettings();
4477
+ }
4478
+ reloadScopeFromDisk(scope) {
4479
+ const file = this.forScope(scope);
4480
+ try {
4481
+ if (!fs4.existsSync(file.path)) {
4482
+ file.settings = {};
4483
+ file.originalSettings = {};
4484
+ file.rawJson = void 0;
4485
+ this._merged = this.computeMergedSettings();
4486
+ return;
4487
+ }
4488
+ const content = fs4.readFileSync(file.path, "utf-8");
4489
+ const parsed = JSON.parse((0, import_strip_json_comments.default)(content));
4490
+ if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
4491
+ const resolved = resolveEnvVarsInObject(
4492
+ parsed,
4493
+ getHomeEnvFallbackVars((message) => debugLogger2.warn(message))
4494
+ );
4495
+ file.settings = resolved;
4496
+ file.originalSettings = structuredClone(parsed);
4497
+ file.rawJson = content;
4498
+ }
4499
+ } catch (err) {
4500
+ debugLogger2.warn(
4501
+ `reloadScopeFromDisk(${scope}): ${getErrorMessage(err)}`
4502
+ );
4503
+ }
4504
+ this._merged = this.computeMergedSettings();
4505
+ }
4506
+ /**
4507
+ * Get user-level hooks from user settings (not merged with workspace).
4508
+ * These hooks should always be loaded regardless of folder trust.
4509
+ */
4510
+ getUserHooks() {
4511
+ return this.user.settings.hooks;
4512
+ }
4513
+ /**
4514
+ * Get project-level hooks from workspace settings (not merged).
4515
+ * Returns undefined if workspace is not trusted (hooks filtered out).
4516
+ */
4517
+ getProjectHooks() {
4518
+ if (!this.isTrusted) {
4519
+ return void 0;
4520
+ }
4521
+ return this.workspace.settings.hooks;
4522
+ }
4523
+ };
4524
+ function createMinimalSettings() {
4525
+ const emptySettingsFile = {
4526
+ path: "",
4527
+ settings: {},
4528
+ originalSettings: {},
4529
+ rawJson: "{}"
4530
+ };
4531
+ return new LoadedSettings(
4532
+ emptySettingsFile,
4533
+ emptySettingsFile,
4534
+ emptySettingsFile,
4535
+ emptySettingsFile,
4536
+ false,
4537
+ /* @__PURE__ */ new Set(),
4538
+ [],
4539
+ void 0,
4540
+ false
4541
+ );
4542
+ }
4543
+ __name(createMinimalSettings, "createMinimalSettings");
4544
+ function detectQwenHomeRedirectWithoutMigration(activeUserSettingsPath) {
4545
+ if (!process2.env["QWEN_HOME"]) {
4546
+ return null;
4547
+ }
4548
+ const activeQwenDir = Storage.getGlobalQwenDir();
4549
+ const savedQwenHome = process2.env["QWEN_HOME"];
4550
+ delete process2.env["QWEN_HOME"];
4551
+ let legacyQwenDir;
4552
+ try {
4553
+ legacyQwenDir = Storage.getGlobalQwenDir();
4554
+ } finally {
4555
+ process2.env["QWEN_HOME"] = savedQwenHome;
4556
+ }
4557
+ if (path3.resolve(activeQwenDir) === path3.resolve(legacyQwenDir)) {
4558
+ return null;
4559
+ }
4560
+ if (fs4.existsSync(activeUserSettingsPath)) {
4561
+ return null;
4562
+ }
4563
+ const legacyUserSettings = path3.join(legacyQwenDir, "settings.json");
4564
+ if (!fs4.existsSync(legacyUserSettings)) {
4565
+ return null;
4566
+ }
4567
+ return `QWEN_HOME points to "${activeQwenDir}" but no settings.json was found there. Existing config remains at "${legacyQwenDir}" \u2014 OAuth tokens, settings, memory, extensions, and skills are not auto-migrated. Copy them manually if you want them to apply at the new location.`;
4568
+ }
4569
+ __name(detectQwenHomeRedirectWithoutMigration, "detectQwenHomeRedirectWithoutMigration");
4570
+ var CORRUPTED_SUFFIX = ".corrupted";
4571
+ function loadSettings(workspaceDir = process2.cwd(), consumeCorruptionEnvVars = true) {
4572
+ const opts = typeof consumeCorruptionEnvVars === "object" ? consumeCorruptionEnvVars : { consumeCorruptionEnvVars };
4573
+ preResolveHomeEnvOverrides();
4574
+ const userSettingsPath = getUserSettingsPath();
4575
+ const qwenHomeRedirectWarning = detectQwenHomeRedirectWithoutMigration(userSettingsPath);
4576
+ let systemSettings = {};
4577
+ let systemDefaultSettings = {};
4578
+ let userSettings = {};
4579
+ let workspaceSettings = {};
4580
+ const settingsErrors = [];
4581
+ const systemSettingsPath = getSystemSettingsPath();
4582
+ const systemDefaultsPath = getSystemDefaultsPath();
4583
+ const migratedInMemorScopes = /* @__PURE__ */ new Set();
4584
+ const resolvedWorkspaceDir = path3.resolve(workspaceDir);
4585
+ const resolvedHomeDir = path3.resolve(homedir2());
4586
+ let realWorkspaceDir = resolvedWorkspaceDir;
4587
+ try {
4588
+ realWorkspaceDir = fs4.realpathSync(resolvedWorkspaceDir);
4589
+ } catch (_e) {
4590
+ }
4591
+ const realHomeDir = fs4.realpathSync(resolvedHomeDir);
4592
+ const workspaceSettingsPath = new Storage(
4593
+ workspaceDir
4594
+ ).getWorkspaceSettingsPath();
4595
+ const loadAndMigrate = /* @__PURE__ */ __name((filePath, scope) => {
4596
+ try {
4597
+ if (fs4.existsSync(filePath)) {
4598
+ const content = fs4.readFileSync(filePath, "utf-8");
4599
+ let rawSettings;
4600
+ const corruptedPath = `${filePath}${CORRUPTED_SUFFIX}`;
4601
+ let corruptedSaved = false;
4602
+ let recoveredFromEnvVar = null;
4603
+ try {
4604
+ rawSettings = JSON.parse((0, import_strip_json_comments.default)(content));
4605
+ } catch (parseError) {
4606
+ debugLogger2.warn(
4607
+ `Settings file ${filePath} has invalid JSON (${getErrorMessage(parseError)}). Resetting to empty settings.`
4608
+ );
4609
+ try {
4610
+ fs4.copyFileSync(filePath, corruptedPath);
4611
+ corruptedSaved = true;
4612
+ } catch (copyError) {
4613
+ debugLogger2.warn(
4614
+ `Failed to copy corrupted file: ${getErrorMessage(copyError)}`
4615
+ );
4616
+ }
4617
+ if (!rawSettings) {
4618
+ const warningMsg = `Settings file ${filePath} has invalid JSON. Your settings have been reset.`;
4619
+ debugLogger2.warn(warningMsg);
4620
+ if (corruptedSaved) {
4621
+ try {
4622
+ fs4.writeFileSync(filePath, "{}", "utf-8");
4623
+ } catch {
4624
+ }
4625
+ }
4626
+ return {
4627
+ settings: {},
4628
+ migrationWarnings: [],
4629
+ corruptedPath: corruptedSaved ? corruptedPath : void 0,
4630
+ wasRecovered: false
4631
+ };
4632
+ }
4633
+ }
4634
+ const envCorruptedPath = process2.env[ENV_CORRUPTED_PATH];
4635
+ if ((opts.consumeCorruptionEnvVars ?? true) && envCorruptedPath && envCorruptedPath === corruptedPath && scope === "User" /* User */) {
4636
+ corruptedSaved = true;
4637
+ recoveredFromEnvVar = process2.env[ENV_WAS_RECOVERED] === "1";
4638
+ delete process2.env[ENV_CORRUPTED_PATH];
4639
+ delete process2.env[ENV_WAS_RECOVERED];
4640
+ }
4641
+ if (typeof rawSettings !== "object" || rawSettings === null || Array.isArray(rawSettings)) {
4642
+ settingsErrors.push({
4643
+ message: "Settings file is not a valid JSON object.",
4644
+ path: filePath
4645
+ });
4646
+ return { settings: {} };
4647
+ }
4648
+ let settingsObject = rawSettings;
4649
+ const hasVersionKey = SETTINGS_VERSION_KEY in settingsObject;
4650
+ const versionValue = settingsObject[SETTINGS_VERSION_KEY];
4651
+ const hasInvalidVersion = hasVersionKey && typeof versionValue !== "number";
4652
+ const hasLegacyNumericVersion = typeof versionValue === "number" && versionValue < SETTINGS_VERSION;
4653
+ let migrationWarnings;
4654
+ const persistSettingsObject = /* @__PURE__ */ __name((warningPrefix) => {
4655
+ try {
4656
+ const written = updateSettingsFilePreservingFormat(
4657
+ filePath,
4658
+ settingsObject,
4659
+ true
4660
+ );
4661
+ if (!written) {
4662
+ debugLogger2.error(
4663
+ `${warningPrefix}: updateSettingsFilePreservingFormat returned false for ${filePath}`
4664
+ );
4665
+ }
4666
+ } catch (e) {
4667
+ debugLogger2.error(`${warningPrefix}: ${getErrorMessage(e)}`);
4668
+ }
4669
+ }, "persistSettingsObject");
4670
+ if (needsMigration(settingsObject)) {
4671
+ const migrationResult = runMigrations(settingsObject, scope);
4672
+ if (migrationResult.executedMigrations.length > 0) {
4673
+ settingsObject = migrationResult.settings;
4674
+ migrationWarnings = migrationResult.warnings;
4675
+ persistSettingsObject("Error migrating settings file on disk");
4676
+ } else if ((hasLegacyNumericVersion || hasInvalidVersion) && !corruptedSaved) {
4677
+ settingsObject[SETTINGS_VERSION_KEY] = SETTINGS_VERSION;
4678
+ debugLogger2.warn(
4679
+ `Settings version metadata in ${filePath} could not be migrated by any registered migration. Normalizing ${SETTINGS_VERSION_KEY} to ${SETTINGS_VERSION}.`
4680
+ );
4681
+ persistSettingsObject("Error normalizing settings version on disk");
4682
+ }
4683
+ } else if ((!hasVersionKey || hasInvalidVersion || hasLegacyNumericVersion) && !corruptedSaved) {
4684
+ settingsObject[SETTINGS_VERSION_KEY] = SETTINGS_VERSION;
4685
+ persistSettingsObject("Error normalizing settings version on disk");
4686
+ }
4687
+ const result = {
4688
+ settings: settingsObject,
4689
+ rawJson: content,
4690
+ migrationWarnings: migrationWarnings ?? []
4691
+ };
4692
+ if (corruptedSaved) {
4693
+ result.corruptedPath = corruptedPath;
4694
+ result.wasRecovered = recoveredFromEnvVar ?? false;
4695
+ }
4696
+ return result;
4697
+ }
4698
+ } catch (error) {
4699
+ settingsErrors.push({
4700
+ message: getErrorMessage(error),
4701
+ path: filePath
4702
+ });
4703
+ }
4704
+ return { settings: {} };
4705
+ }, "loadAndMigrate");
4706
+ const systemResult = loadAndMigrate(systemSettingsPath, "System" /* System */);
4707
+ const systemDefaultsResult = loadAndMigrate(
4708
+ systemDefaultsPath,
4709
+ "SystemDefaults" /* SystemDefaults */
4710
+ );
4711
+ const userResult = loadAndMigrate(userSettingsPath, "User" /* User */);
4712
+ let workspaceResult = {
4713
+ settings: {},
4714
+ rawJson: void 0
4715
+ };
4716
+ const workspaceSettingsActive = realWorkspaceDir !== realHomeDir;
4717
+ if (workspaceSettingsActive) {
4718
+ workspaceResult = loadAndMigrate(
4719
+ workspaceSettingsPath,
4720
+ "Workspace" /* Workspace */
4721
+ );
4722
+ }
4723
+ const systemOriginalSettings = structuredClone(systemResult.settings);
4724
+ const systemDefaultsOriginalSettings = structuredClone(
4725
+ systemDefaultsResult.settings
4726
+ );
4727
+ const userOriginalSettings = structuredClone(userResult.settings);
4728
+ const workspaceOriginalSettings = structuredClone(workspaceResult.settings);
4729
+ const homeEnvFallback = getHomeEnvFallbackVars(
4730
+ (message) => debugLogger2.warn(message)
4731
+ );
4732
+ systemSettings = resolveEnvVarsInObject(
4733
+ systemResult.settings,
4734
+ homeEnvFallback
4735
+ );
4736
+ systemDefaultSettings = resolveEnvVarsInObject(
4737
+ systemDefaultsResult.settings,
4738
+ homeEnvFallback
4739
+ );
4740
+ userSettings = resolveEnvVarsInObject(userResult.settings, homeEnvFallback);
4741
+ workspaceSettings = resolveEnvVarsInObject(
4742
+ workspaceResult.settings,
4743
+ homeEnvFallback
4744
+ );
4745
+ if (userSettings.ui?.theme === "VS") {
4746
+ userSettings.ui.theme = DEFAULT_LIGHT_THEME_NAME;
4747
+ } else if (userSettings.ui?.theme === "VS2015") {
4748
+ userSettings.ui.theme = DEFAULT_DARK_THEME_NAME;
4749
+ }
4750
+ if (workspaceSettings.ui?.theme === "VS") {
4751
+ workspaceSettings.ui.theme = DEFAULT_LIGHT_THEME_NAME;
4752
+ } else if (workspaceSettings.ui?.theme === "VS2015") {
4753
+ workspaceSettings.ui.theme = DEFAULT_DARK_THEME_NAME;
4754
+ }
4755
+ const initialTrustCheckSettings = customDeepMerge(
4756
+ getMergeStrategyForPath,
4757
+ {},
4758
+ systemSettings,
4759
+ userSettings
4760
+ );
4761
+ const isTrusted = isWorkspaceTrusted(
4762
+ initialTrustCheckSettings,
4763
+ void 0,
4764
+ realWorkspaceDir
4765
+ ).isTrusted ?? true;
4766
+ const tempMergedSettings = mergeSettings(
4767
+ systemSettings,
4768
+ systemDefaultSettings,
4769
+ userSettings,
4770
+ workspaceSettings,
4771
+ isTrusted
4772
+ );
4773
+ if (!opts.skipLoadEnvironment) {
4774
+ loadEnvironment(tempMergedSettings, workspaceDir);
4775
+ }
4776
+ if (settingsErrors.length > 0) {
4777
+ const errorMessages = settingsErrors.map(
4778
+ (error) => `Error in ${error.path}: ${error.message}`
4779
+ );
4780
+ throw new FatalConfigError(
4781
+ `${errorMessages.join("\n")}
4782
+ Please fix the configuration file(s) and try again.`
4783
+ );
4784
+ }
4785
+ const allMigrationWarnings = [
4786
+ ...qwenHomeRedirectWarning ? [qwenHomeRedirectWarning] : [],
4787
+ ...systemResult.migrationWarnings ?? [],
4788
+ ...systemDefaultsResult.migrationWarnings ?? [],
4789
+ ...userResult.migrationWarnings ?? [],
4790
+ ...workspaceResult.migrationWarnings ?? []
4791
+ ];
4792
+ return new LoadedSettings(
4793
+ {
4794
+ path: systemSettingsPath,
4795
+ settings: systemSettings,
4796
+ originalSettings: systemOriginalSettings,
4797
+ rawJson: systemResult.rawJson
4798
+ },
4799
+ {
4800
+ path: systemDefaultsPath,
4801
+ settings: systemDefaultSettings,
4802
+ originalSettings: systemDefaultsOriginalSettings,
4803
+ rawJson: systemDefaultsResult.rawJson
4804
+ },
4805
+ {
4806
+ path: userSettingsPath,
4807
+ settings: userSettings,
4808
+ originalSettings: userOriginalSettings,
4809
+ rawJson: userResult.rawJson
4810
+ },
4811
+ {
4812
+ path: workspaceSettingsPath,
4813
+ settings: workspaceSettings,
4814
+ originalSettings: workspaceOriginalSettings,
4815
+ rawJson: workspaceResult.rawJson
4816
+ },
4817
+ isTrusted,
4818
+ migratedInMemorScopes,
4819
+ allMigrationWarnings,
4820
+ userResult.corruptedPath,
4821
+ userResult.wasRecovered ?? false,
4822
+ workspaceSettingsActive
4823
+ );
4824
+ }
4825
+ __name(loadSettings, "loadSettings");
4826
+ function createSettingsUpdate(key, value) {
4827
+ const root = {};
4828
+ setNestedPropertySafe(root, key, value);
4829
+ return root;
4830
+ }
4831
+ __name(createSettingsUpdate, "createSettingsUpdate");
4832
+ function saveSettings(settingsFile, updates = settingsFile.originalSettings, replacePath = [], opts = {}) {
4833
+ try {
4834
+ const dirPath = path3.dirname(settingsFile.path);
4835
+ if (!fs4.existsSync(dirPath)) {
4836
+ fs4.mkdirSync(dirPath, { recursive: true });
4837
+ }
4838
+ const written = updateSettingsFilePreservingFormat(
4839
+ settingsFile.path,
4840
+ updates,
4841
+ false,
4842
+ replacePath
4843
+ );
4844
+ if (!written) {
4845
+ const message = `saveSettings: updateSettingsFilePreservingFormat returned false for ${settingsFile.path}`;
4846
+ if (opts.throwOnWriteFailure) {
4847
+ throw new Error(message);
4848
+ }
4849
+ debugLogger2.error(message);
4850
+ }
4851
+ } catch (error) {
4852
+ debugLogger2.error("Error saving user settings file.");
4853
+ debugLogger2.error(error instanceof Error ? error.message : String(error));
4854
+ throw error;
4855
+ }
4856
+ }
4857
+ __name(saveSettings, "saveSettings");
4858
+
4859
+ export {
4860
+ getPersistScopeForModelSelection,
4861
+ TOGGLE_TYPES,
4862
+ OUTPUT_LANGUAGE_AUTO,
4863
+ isAutoLanguage,
4864
+ resolveOutputLanguage,
4865
+ getOutputLanguageFilePath,
4866
+ updateOutputLanguageFile,
4867
+ writeOutputLanguageAndRegisterPath,
4868
+ initializeLlmOutputLanguage,
4869
+ getFlattenedSchema,
4870
+ getSettingDefinition,
4871
+ requiresRestart,
4872
+ getDefaultValue,
4873
+ getNestedValue,
4874
+ getNestedProperty,
4875
+ getEffectiveValue,
4876
+ validateSettingValue,
4877
+ getDialogSettingKeys,
4878
+ settingExistsInScope,
4879
+ setPendingSettingValue,
4880
+ setPendingSettingValueAny,
4881
+ getRestartRequiredFromModified,
4882
+ saveModifiedSettings,
4883
+ getDisplayValue,
4884
+ isDefaultValue,
4885
+ backupSettingsFile,
4886
+ restoreSettingsFromBackup,
4887
+ cleanupSettingsBackup,
4888
+ SETTINGS_DIRECTORY_NAME,
4889
+ preResolveHomeEnvOverrides,
4890
+ resetHomeEnvBootstrapForTesting,
4891
+ resetEnvironmentTrackingForTesting,
4892
+ getHomeEnvFallbackVars,
4893
+ setUpCloudShellEnvironment,
4894
+ loadEnvironment,
4895
+ reloadEnvironment,
4896
+ getUserSettingsPath,
4897
+ getUserSettingsDir,
4898
+ SETTINGS_VERSION,
4899
+ SETTINGS_VERSION_KEY,
4900
+ migrateLegacyPermissions,
4901
+ SettingScope,
4902
+ getSettingsWarnings,
4903
+ LoadedSettings,
4904
+ createMinimalSettings,
4905
+ CORRUPTED_SUFFIX,
4906
+ loadSettings,
4907
+ saveSettings
4908
+ };
4909
+ /**
4910
+ * @license
4911
+ * Copyright 2025 Qwen Team
4912
+ * SPDX-License-Identifier: Apache-2.0
4913
+ */
4914
+ /**
4915
+ * @license
4916
+ * Copyright 2025 Google LLC
4917
+ * SPDX-License-Identifier: Apache-2.0
4918
+ */
4919
+ /**
4920
+ * @license
4921
+ * Copyright 2025 Qwen team
4922
+ * SPDX-License-Identifier: Apache-2.0
4923
+ */