@qwen-code/qwen-code 0.19.11-nightly.20260718.767a32484 → 0.19.11
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.
- package/bundled/qc-helper/docs/configuration/model-providers.md +0 -11
- package/bundled/qc-helper/docs/configuration/settings.md +9 -9
- package/bundled/qc-helper/docs/features/channels/overview.md +11 -24
- package/bundled/qc-helper/docs/qwen-serve-deploy-local.md +7 -42
- package/bundled/qc-helper/docs/qwen-serve.md +40 -66
- package/bundled/qc-helper/docs/reference/keyboard-shortcuts.md +1 -1
- package/chunks/{MaxSizedBox-ZFHA4QEQ.js → MaxSizedBox-L6TGYDM3.js} +27 -27
- package/chunks/{StandaloneSessionPicker-YRWOQVQA.js → StandaloneSessionPicker-AKEQHU5P.js} +39 -39
- package/chunks/{acpAgent-U2P2IESZ.js → acpAgent-PTNBGJDN.js} +228 -501
- package/chunks/{agent-GQXX6X5Y.js → agent-AJOMOZPJ.js} +24 -24
- package/chunks/{agent-headless-3572NBCK.js → agent-headless-CYKOMAXS.js} +24 -24
- package/chunks/{anthropicContentGenerator-Y65DMM64.js → anthropicContentGenerator-HBRXJSP4.js} +6 -29
- package/chunks/{artifact-tool-WR7I2XON.js → artifact-tool-MIZLXBLF.js} +2 -2
- package/chunks/{askUserQuestion-B4FUTPHD.js → askUserQuestion-XMJVPNWM.js} +44 -2
- package/chunks/{bridge-N5RMGD7G.js → bridge-QKISWPCI.js} +29 -29
- package/chunks/{ca-M3GQQ26D.js → ca-CODEN7TD.js} +2 -2
- package/chunks/{channel-worker-group-HZVJKLGX.js → channel-worker-group-SOUWTFHC.js} +4 -4
- package/chunks/{channel-worker-manager-3WDK4HNS.js → channel-worker-manager-LOVSV3WW.js} +4 -4
- package/chunks/{channel-worker-supervisor-U2W6Q5F7.js → channel-worker-supervisor-XW6MHJBQ.js} +3 -3
- package/chunks/{chunk-3DJY4IFO.js → chunk-2FO5GFKZ.js} +1 -1
- package/chunks/chunk-2HRYPZT5.js +25 -0
- package/chunks/{chunk-LSFVUGXT.js → chunk-2OG646IN.js} +3 -3
- package/chunks/{chunk-OSSDHJDU.js → chunk-2Z5UX4KI.js} +1 -1
- package/chunks/{chunk-O2K6BLPZ.js → chunk-3AYLDB6B.js} +1 -1
- package/chunks/{chunk-MI2V6IWU.js → chunk-3DWIFU7L.js} +1 -1
- package/chunks/{chunk-AEKYFMYE.js → chunk-3M75MSVA.js} +1 -1
- package/chunks/{chunk-HIMIG6PI.js → chunk-4AGTGMCQ.js} +11 -11
- package/chunks/{chunk-V2FRLYHN.js → chunk-4BJVSIS3.js} +2 -2
- package/chunks/{chunk-64XGKNSI.js → chunk-4HUDDSO5.js} +3 -3
- package/chunks/{chunk-FSJGBUQI.js → chunk-4IJMTYCB.js} +28 -817
- package/chunks/{chunk-TYQMGACW.js → chunk-4LD5APFL.js} +0 -1
- package/chunks/{chunk-2XRGFF5K.js → chunk-4ROQY23C.js} +6 -6
- package/chunks/{chunk-KEVDWH3U.js → chunk-574PTLMU.js} +55 -207
- package/chunks/{chunk-Y72B36CP.js → chunk-5CNYZ7TN.js} +7 -7
- package/chunks/{chunk-DPSHAB47.js → chunk-5VWYKIMW.js} +1 -1
- package/chunks/{chunk-OXOEOOQU.js → chunk-5Z452MMD.js} +37 -64
- package/chunks/{chunk-U6OPT4H2.js → chunk-6EDDYPI3.js} +4 -4
- package/chunks/{chunk-XA7OKDWM.js → chunk-6WGZDD67.js} +2 -2
- package/chunks/{chunk-IENO2YAT.js → chunk-6WPJRYLZ.js} +3 -3
- package/chunks/{chunk-PWYLF7SS.js → chunk-6Y6ORE2G.js} +10 -7
- package/chunks/{chunk-UE5TZ55T.js → chunk-7NFB7LBQ.js} +0 -8
- package/chunks/{chunk-3CL5SEDT.js → chunk-7NMU22Q3.js} +6 -6
- package/chunks/{chunk-ECZFTRTP.js → chunk-7ZYHLN67.js} +75 -26
- package/chunks/{chunk-LKR7QQ63.js → chunk-A2HWMJVO.js} +3 -3
- package/chunks/{chunk-SGPFJMRP.js → chunk-ARKANCNX.js} +1 -1
- package/chunks/{chunk-5QRK2VZG.js → chunk-AU3MFJPL.js} +8 -8
- package/chunks/{chunk-R3VWUU2E.js → chunk-AVEOWVOM.js} +1 -1
- package/chunks/{chunk-6ITLKEOZ.js → chunk-BUAXJVNP.js} +3 -3
- package/chunks/{chunk-U4FJ26D2.js → chunk-BXBDC476.js} +2 -2
- package/chunks/{chunk-ZTSDZQZO.js → chunk-BYB3GF2T.js} +3 -130
- package/chunks/{chunk-4JBTZBCW.js → chunk-BYH2JRK3.js} +5 -5
- package/chunks/{chunk-4HCYNDMG.js → chunk-CAHY5ABC.js} +1 -1
- package/chunks/{chunk-JBAQ5BYX.js → chunk-CF5VJRGC.js} +1 -1
- package/chunks/{chunk-3NWYQ7FX.js → chunk-CH2XVB47.js} +2 -2
- package/chunks/{chunk-FHUXCRAF.js → chunk-CITWNIXJ.js} +4 -4
- package/chunks/{chunk-6YIRYY3P.js → chunk-CPFIQVZJ.js} +1 -1
- package/chunks/{chunk-EKG3BHQY.js → chunk-DOPUYNXO.js} +3 -3
- package/chunks/{chunk-VNN3JNYT.js → chunk-F333XEAU.js} +1 -1
- package/chunks/{chunk-FUTHTZVV.js → chunk-FBU7WRZI.js} +1 -1
- package/chunks/{chunk-OBYAX5QZ.js → chunk-FMNYGWOD.js} +6 -6
- package/chunks/{chunk-FZRES4GK.js → chunk-FNX3LGAK.js} +1 -1
- package/chunks/{chunk-F2RR4HO2.js → chunk-FSA7ERJ2.js} +1 -1
- package/chunks/{chunk-OPVIKLKK.js → chunk-FXKOAEXP.js} +14 -14
- package/chunks/{chunk-U2XBPBO3.js → chunk-FYQ22K62.js} +3 -4
- package/chunks/{chunk-KTKETHMY.js → chunk-GLNHPRZT.js} +1 -1
- package/chunks/{chunk-C5NSOSF4.js → chunk-GNBBAQ6J.js} +13 -30
- package/chunks/{chunk-GAQOQMHY.js → chunk-HASK4LMI.js} +6 -6
- package/chunks/{chunk-GG6RSNWS.js → chunk-JD44PNGJ.js} +4 -9
- package/chunks/{chunk-ANRANWVE.js → chunk-JGUT3LWZ.js} +1 -4
- package/chunks/{chunk-336KPNNV.js → chunk-JWICTOXS.js} +2 -2
- package/chunks/{chunk-57RWF6R7.js → chunk-K7VD4XLT.js} +1 -1
- package/chunks/{chunk-XSPFQQRE.js → chunk-KKRTYONM.js} +3 -3
- package/chunks/{chunk-JDIUK2Y3.js → chunk-KSZKSE23.js} +1 -1
- package/chunks/{chunk-HQXQCIT2.js → chunk-KXYJPRT5.js} +5 -25
- package/chunks/{chunk-X6YQWCIJ.js → chunk-L4Y55KIS.js} +1 -1
- package/chunks/{chunk-335ECZTA.js → chunk-L5U3ELZX.js} +1 -1
- package/chunks/{chunk-4CHDYDG3.js → chunk-LQIDP6K5.js} +1 -1
- package/chunks/{chunk-5O34IMY5.js → chunk-LS56TX3M.js} +1 -1
- package/chunks/{chunk-HCCR43AY.js → chunk-LXLTBIDP.js} +1 -1
- package/chunks/{chunk-66IT3EY2.js → chunk-MQOVXZAF.js} +48 -164
- package/chunks/{chunk-JUVTV4NF.js → chunk-N5VIZTND.js} +1 -1
- package/chunks/{chunk-3YJZ6WNY.js → chunk-NJOFRXTM.js} +593 -1317
- package/chunks/{chunk-CZSAIBUB.js → chunk-NVTQZXR7.js} +7 -7
- package/chunks/{chunk-G274ETR6.js → chunk-OKAIGAYW.js} +3 -3
- package/chunks/{chunk-TVYM3Q3J.js → chunk-OWT2CAEQ.js} +7 -100
- package/chunks/{chunk-T3W4SDRB.js → chunk-PZL23GTM.js} +0 -2
- package/chunks/{chunk-XAAEDML2.js → chunk-Q6TUALBE.js} +1 -1
- package/chunks/chunk-QAW7PIHT.js +168 -0
- package/chunks/{chunk-HT7L5FCZ.js → chunk-QDJ2R4CM.js} +3 -3
- package/chunks/{chunk-IX7O5WI3.js → chunk-QOTQH4CB.js} +8 -23
- package/chunks/{chunk-S4N3G4BB.js → chunk-QYG4BY6Q.js} +1 -1
- package/chunks/{chunk-4XCVGEEX.js → chunk-RCEEXIOO.js} +3 -3
- package/chunks/{chunk-IK5TYYVR.js → chunk-RP5R7MB6.js} +12 -12
- package/chunks/{chunk-U2LNTX56.js → chunk-RTV5TUYB.js} +1 -1
- package/chunks/{chunk-K2GBHL73.js → chunk-S63Z5V42.js} +4 -4
- package/chunks/{chunk-J2MKYJKZ.js → chunk-SPJQO2CE.js} +2 -2
- package/chunks/{chunk-LTD3CHGS.js → chunk-SQ5BK3GO.js} +1 -1
- package/chunks/{chunk-W7H223ZZ.js → chunk-STZQCNN3.js} +1 -1
- package/chunks/{chunk-2YSZE74N.js → chunk-SY7NEYX4.js} +2 -2
- package/chunks/{chunk-GA3WUOO7.js → chunk-T76DGHGG.js} +5 -5
- package/chunks/{chunk-QPRDXJIC.js → chunk-THXDQSTA.js} +29 -30
- package/chunks/{chunk-DASWPKXR.js → chunk-TIOE2OPE.js} +2 -2
- package/chunks/{chunk-F5XYN5VF.js → chunk-TL4R5TRK.js} +2 -2
- package/chunks/{chunk-4RPARQ5D.js → chunk-TV6K2FB4.js} +2 -2
- package/chunks/{chunk-JCJPZMJV.js → chunk-TYLOFD2U.js} +1 -1
- package/chunks/{chunk-632KROVU.js → chunk-V2DXTLUE.js} +3 -3
- package/chunks/{chunk-URP6O2PP.js → chunk-VFX7EZIR.js} +1 -1
- package/chunks/{chunk-CMVFTMRD.js → chunk-VRR65QYW.js} +1 -1
- package/chunks/{chunk-VAJS2E2D.js → chunk-WIEO4CWB.js} +1 -1
- package/chunks/{chunk-OCQ2QTFJ.js → chunk-WMNXXRGL.js} +25 -25
- package/chunks/{chunk-WEPXQKGP.js → chunk-WTNZ6DXB.js} +1 -1
- package/chunks/{chunk-LGY7PG7Q.js → chunk-Y3KFTWVX.js} +5 -5
- package/chunks/{chunk-4CA4WUMS.js → chunk-Y66VOBRP.js} +2 -2
- package/chunks/{chunk-IVKLDHU6.js → chunk-YFVO2HOR.js} +4 -4
- package/chunks/{chunk-GUNTLALV.js → chunk-YGYSMY2R.js} +47 -61
- package/chunks/{chunk-3X2CFOUM.js → chunk-YLSSEUAM.js} +274 -345
- package/chunks/{chunk-6Z5NRZVU.js → chunk-ZIXOTAPZ.js} +3 -92
- package/chunks/{chunk-Z2LBNX3R.js → chunk-ZPAHLFZU.js} +2 -2
- package/chunks/{chunk-ATJ7H5AS.js → chunk-ZQIYGQCG.js} +84 -230
- package/chunks/{computer-use-UIFBH25Y.js → computer-use-VZZMJEQ7.js} +24 -24
- package/chunks/{config-utils-2LHI7JXU.js → config-utils-IQQT3GCI.js} +3 -3
- package/chunks/{contextCommand-VGZHKEH2.js → contextCommand-PORKIZSY.js} +26 -26
- package/chunks/{create-sub-session-DIJOV6HV.js → create-sub-session-EAB2U5XW.js} +2 -2
- package/chunks/{create-sub-session-OFVI4H6I.js → create-sub-session-XVFKHAFW.js} +24 -24
- package/chunks/{cron-create-34GVLSW5.js → cron-create-GMVKSXZT.js} +4 -4
- package/chunks/{cron-delete-NMVKUL45.js → cron-delete-VDZKUAVK.js} +4 -4
- package/chunks/{cron-list-277ZOR7L.js → cron-list-UG7C7RAR.js} +4 -4
- package/chunks/{daemon-72MEV3TY.js → daemon-IHR6WSPB.js} +14 -59
- package/chunks/{daemon-status-provider-ZJJ6AOG2.js → daemon-status-provider-ABLBA3UJ.js} +33 -33
- package/chunks/{de-56TMDIKS.js → de-NOXPCSYN.js} +2 -2
- package/chunks/{dist-ILVRL322.js → dist-4WCQEZIN.js} +1 -1
- package/chunks/{dist-SVHAGOLN.js → dist-DOPL5LSQ.js} +1 -1
- package/chunks/{dist-LQHDHBPP.js → dist-GXJVCOS7.js} +1 -1
- package/chunks/{dist-KEZ5JWH4.js → dist-PX2ERZ6I.js} +2 -2
- package/chunks/{dist-G6QCGO2K.js → dist-V2YVLYOW.js} +1 -1
- package/chunks/{dist-4W3UGU2A.js → dist-VYWUKURL.js} +1 -1
- package/chunks/{earlyInputCapture-NPF4PVUE.js → earlyInputCapture-DJCU5SPU.js} +25 -25
- package/chunks/{edit-F7CT6NZH.js → edit-O4VV5AO6.js} +25 -25
- package/chunks/{en-AXS2N62H.js → en-JFVULHTB.js} +3 -6
- package/chunks/{enter-worktree-Z4ET3MZL.js → enter-worktree-XQYDDI2I.js} +24 -24
- package/chunks/{enterPlanMode-VYR7T5NX.js → enterPlanMode-CESVMQBV.js} +27 -25
- package/chunks/{environment-MBTXL57Q.js → environment-5KXOLZNR.js} +26 -26
- package/chunks/{errors-HN36J4FR.js → errors-EBUTCBAQ.js} +26 -26
- package/chunks/{exit-worktree-RU7JYFMF.js → exit-worktree-MWGNSPNX.js} +24 -24
- package/chunks/exitPlanMode-HYQL4XMB.js +909 -0
- package/chunks/{fast-path-RFT3CDNZ.js → fast-path-6V73F2T2.js} +2 -2
- package/chunks/{fr-ZJPE5OVN.js → fr-HVFNU42H.js} +2 -2
- package/chunks/{gemini-37NTNKZF.js → gemini-TEFJZCIZ.js} +70 -98
- package/chunks/{geminiContentGenerator-76HNOYNQ.js → geminiContentGenerator-M56BEXWI.js} +5 -5
- package/chunks/{glob-DWD7JCZE.js → glob-DMYFEEAZ.js} +25 -30
- package/chunks/{grep-NJRXFV5C.js → grep-GYE47MD4.js} +25 -30
- package/chunks/{handleAutoUpdate-7GBDXW6U.js → handleAutoUpdate-OLUY4L7L.js} +28 -28
- package/chunks/{i18n-MYD5VGED.js → i18n-462JC7IY.js} +25 -25
- package/chunks/{initializer-4DHPNAZG.js → initializer-Y7WD4D2I.js} +30 -30
- package/chunks/{installationInfo-RWOW6BZO.js → installationInfo-I2Z3BKCE.js} +25 -25
- package/chunks/{ja-57GG4QSL.js → ja-PY5AF544.js} +2 -2
- package/chunks/{keychain-token-storage-K23CFKGN.js → keychain-token-storage-AU22CQI6.js} +2 -2
- package/chunks/{list-MMCNNHE7.js → list-XQ5MSAXP.js} +32 -32
- package/chunks/{loadedSettingsAdapter-UK4JWPYU.js → loadedSettingsAdapter-TQOWYE7C.js} +29 -29
- package/chunks/{loop-wakeup-KCLY4XQM.js → loop-wakeup-QXWZSCYM.js} +5 -5
- package/chunks/{ls-UNI4LAJ3.js → ls-2OUTG3IZ.js} +4 -4
- package/chunks/{lsp-HPACPJAN.js → lsp-4VMNXZW6.js} +2 -2
- package/chunks/{mcp-AFCVA4UV.js → mcp-22LLEEVM.js} +29 -29
- package/chunks/{monitor-RK637BNO.js → monitor-VKG324X3.js} +24 -24
- package/chunks/nonInteractiveCli-UMYUT643.js +129 -0
- package/chunks/{notebook-edit-LY2X3CMF.js → notebook-edit-FJJRBQ3S.js} +25 -25
- package/chunks/{openaiContentGenerator-MDYDW7IA.js → openaiContentGenerator-4T2OGTR5.js} +13 -13
- package/chunks/{pidfile-2NUYRLKB.js → pidfile-XL3MDCBM.js} +25 -25
- package/chunks/{processUtils-7QH3VBVN.js → processUtils-7UCFN34D.js} +2 -2
- package/chunks/{pt-MCGZ3RZ4.js → pt-A3PRQXMU.js} +2 -2
- package/chunks/{qwenContentGenerator-3HT4TVDL.js → qwenContentGenerator-PQUXJDIZ.js} +26 -26
- package/chunks/{qwenOAuth2-3A4TJVZT.js → qwenOAuth2-LEYOU7RU.js} +5 -5
- package/chunks/{read-file-NE4FREFR.js → read-file-3EYQML7E.js} +10 -10
- package/chunks/{read-mcp-resource-C6NT52T6.js → read-mcp-resource-JUFHCKSC.js} +2 -2
- package/chunks/{record-artifact-ENCU7QCL.js → record-artifact-DWSMBXYY.js} +2 -2
- package/chunks/{ripGrep-DBLXWQCJ.js → ripGrep-S6WPGQNA.js} +24 -24
- package/chunks/{ru-GA3NZW5Q.js → ru-IBCWUTOC.js} +2 -2
- package/chunks/{run-qwen-serve-3NOCI6IL.js → run-qwen-serve-NSKBT3HU.js} +36 -52
- package/chunks/{runtime-ZI7DSZSS.js → runtime-LG5ZZ3VH.js} +33 -33
- package/chunks/{scheduler-SOVEP5K7.js → scheduler-PLX3VVTB.js} +24 -24
- package/chunks/{send-message-5IFPZTPE.js → send-message-JZ752NWD.js} +3 -3
- package/chunks/{serve-DB6OCXIA.js → serve-S2ZLRZZR.js} +29 -29
- package/chunks/{server-LDY4YVU4.js → server-5ECMV4TM.js} +679 -1413
- package/chunks/{session-D6OG5SKX.js → session-ILSONWG4.js} +66 -93
- package/chunks/{settings-L52ZV5LO.js → settings-BOE7WWSA.js} +28 -28
- package/chunks/{shell-IU27OY3Q.js → shell-VW664JIZ.js} +24 -24
- package/chunks/{skill-BSOJT7LE.js → skill-COHV6625.js} +12 -12
- package/chunks/{spawnChannel-HQAM25DD.js → spawnChannel-5UUR5XOZ.js} +26 -26
- package/chunks/{src-7ZBIMTJZ.js → src-XPKFZ3RN.js} +25 -43
- package/chunks/{standalone-update-77457VVK.js → standalone-update-IOOJ2NZQ.js} +26 -26
- package/chunks/{startInteractiveUI-S7WC6EZG.js → startInteractiveUI-3NGJGXXS.js} +1733 -2194
- package/chunks/{syntheticOutput-ULOVVLY4.js → syntheticOutput-LELY6HAI.js} +3 -3
- package/chunks/{task-create-V2DCDSET.js → task-create-QY3BIMNM.js} +7 -7
- package/chunks/{task-list-DPL5ZN55.js → task-list-D2U47WBJ.js} +5 -5
- package/chunks/{task-stop-CPEQWOT6.js → task-stop-N6E5IYLH.js} +2 -2
- package/chunks/{task-update-ZR45M5Q7.js → task-update-HJCQILLB.js} +7 -7
- package/chunks/{team-create-CO7HSUON.js → team-create-UPLVZQFX.js} +24 -24
- package/chunks/{team-delete-HIWXCWJU.js → team-delete-RCWYDRF4.js} +5 -5
- package/chunks/{team-plan-approval-QDZF5YZY.js → team-plan-approval-F5F432RO.js} +24 -24
- package/chunks/{theme-manager-UTUT3I4P.js → theme-manager-JJE4BJ67.js} +25 -25
- package/chunks/todoWrite-FKCGATPJ.js +531 -0
- package/chunks/{tool-search-2UUORA35.js → tool-search-LRXI4JLZ.js} +11 -11
- package/chunks/{total-session-admission-NTWG6G3F.js → total-session-admission-I4DHN6UP.js} +30 -30
- package/chunks/{trustedFolders-YF37ZCN5.js → trustedFolders-SEH7LNX2.js} +25 -25
- package/chunks/{update-relaunch-QFNHPMZZ.js → update-relaunch-T6ZKBUA6.js} +5 -5
- package/chunks/{updateCheck-B6O42VMD.js → updateCheck-FWLXZPRO.js} +26 -26
- package/chunks/{validateNonInterActiveAuth-SCIWJ4CX.js → validateNonInterActiveAuth-X6K2VWYT.js} +59 -59
- package/chunks/{version-7DA6LAI3.js → version-LIFSOGSE.js} +1 -1
- package/chunks/{web-fetch-B62SJXM2.js → web-fetch-CFCWTCAP.js} +5 -5
- package/chunks/{workflow-Q5WG4THP.js → workflow-NX2DFWGD.js} +25 -25
- package/chunks/{workspace-providers-status-WWLAGWQW.js → workspace-providers-status-VYD6M2NE.js} +32 -32
- package/chunks/{workspace-registration-store-BVYQVY2H.js → workspace-registration-store-2Y6PWOEL.js} +1 -1
- package/chunks/{workspace-registry-OD7ACADD.js → workspace-registry-PKBDBWCA.js} +30 -30
- package/chunks/{workspace-service-4LYJWS63.js → workspace-service-HHORUHTS.js} +35 -35
- package/chunks/{workspace-skills-status-P2AQLZZS.js → workspace-skills-status-O5Q26LIY.js} +30 -30
- package/chunks/{write-file-TEPGXNM3.js → write-file-NVAEZ3J2.js} +25 -58
- package/chunks/{zh-E34DZ2S3.js → zh-PAUNIMHQ.js} +3 -6
- package/chunks/{zh-TW-PEI2ZWUR.js → zh-TW-GNEUIUFF.js} +3 -6
- package/cli.js +11 -11
- package/locales/ca.js +2 -2
- package/locales/de.js +2 -2
- package/locales/en.js +5 -9
- package/locales/fr.js +2 -2
- package/locales/ja.js +2 -2
- package/locales/pt.js +2 -2
- package/locales/ru.js +2 -2
- package/locales/zh-TW.js +3 -8
- package/locales/zh.js +3 -8
- package/package.json +3 -3
- package/web-shell/assets/{arc-Dh-N5oEG.js → arc-H74fE8UR.js} +1 -1
- package/web-shell/assets/{architectureDiagram-3BPJPVTR-DBOQgtfF.js → architectureDiagram-3BPJPVTR-DN3kEq-I.js} +1 -1
- package/web-shell/assets/{blockDiagram-GPEHLZMM-Dyxbuyju.js → blockDiagram-GPEHLZMM-BrqbsFUF.js} +1 -1
- package/web-shell/assets/{c4Diagram-AAUBKEIU-DCJR5HPT.js → c4Diagram-AAUBKEIU-ClFhU1P-.js} +1 -1
- package/web-shell/assets/channel-NFC5ADAa.js +1 -0
- package/web-shell/assets/{chunk-2J33WTMH-BkMUcy3O.js → chunk-2J33WTMH-jCIcG0_M.js} +1 -1
- package/web-shell/assets/{chunk-4BX2VUAB-BSm4pLQk.js → chunk-4BX2VUAB-DSkGe62-.js} +1 -1
- package/web-shell/assets/{chunk-55IACEB6-CuRr-XgW.js → chunk-55IACEB6-BxBHOGTd.js} +1 -1
- package/web-shell/assets/{chunk-727SXJPM-CLv7-w3t.js → chunk-727SXJPM-v_DS7dH7.js} +1 -1
- package/web-shell/assets/{chunk-AQP2D5EJ-Cz_n3-JW.js → chunk-AQP2D5EJ-1RzEKRPq.js} +1 -1
- package/web-shell/assets/{chunk-FMBD7UC4-CFT_62Hq.js → chunk-FMBD7UC4-DyrSbLOX.js} +1 -1
- package/web-shell/assets/{chunk-ND2GUHAM-DWz6DOZ6.js → chunk-ND2GUHAM-BCmdsZiy.js} +1 -1
- package/web-shell/assets/{chunk-QZHKN3VN-C-mRhUzw.js → chunk-QZHKN3VN-CphHGW2D.js} +1 -1
- package/web-shell/assets/classDiagram-4FO5ZUOK-DHgwjz3X.js +1 -0
- package/web-shell/assets/classDiagram-v2-Q7XG4LA2-DHgwjz3X.js +1 -0
- package/web-shell/assets/{cose-bilkent-S5V4N54A-BqCZS3i1.js → cose-bilkent-S5V4N54A-D5kRC7Fi.js} +1 -1
- package/web-shell/assets/{dagre-BM42HDAG-BGasdP8i.js → dagre-BM42HDAG-DIiyJJ0U.js} +1 -1
- package/web-shell/assets/{diagram-2AECGRRQ-BSyLDkMy.js → diagram-2AECGRRQ-BOcuB8xU.js} +1 -1
- package/web-shell/assets/{diagram-5GNKFQAL-CM-ZJxWC.js → diagram-5GNKFQAL-CMgiZ4d3.js} +1 -1
- package/web-shell/assets/{diagram-KO2AKTUF-wJdzt_C5.js → diagram-KO2AKTUF-Ctx8pfN0.js} +1 -1
- package/web-shell/assets/{diagram-LMA3HP47-DiH2fhpL.js → diagram-LMA3HP47-BT5REAWC.js} +1 -1
- package/web-shell/assets/{diagram-OG6HWLK6-BZ9z1ZBl.js → diagram-OG6HWLK6-CJkUqXcp.js} +1 -1
- package/web-shell/assets/{erDiagram-TEJ5UH35-BsyWDziR.js → erDiagram-TEJ5UH35-DyvNNX6v.js} +1 -1
- package/web-shell/assets/{flowDiagram-I6XJVG4X-CzdkxIrq.js → flowDiagram-I6XJVG4X-VBf8_My8.js} +1 -1
- package/web-shell/assets/{ganttDiagram-6RSMTGT7-javIv2r2.js → ganttDiagram-6RSMTGT7-CviJnR7B.js} +1 -1
- package/web-shell/assets/{gitGraphDiagram-PVQCEYII-CjHstylC.js → gitGraphDiagram-PVQCEYII-BY66_Oze.js} +1 -1
- package/web-shell/assets/index-8xIBbHV3.js +1143 -0
- package/web-shell/assets/index-DLyhlZZO.css +5 -0
- package/web-shell/assets/{index-BnJGxpah.js → index-DhYuvi3j.js} +1 -1
- package/web-shell/assets/{infoDiagram-5YYISTIA-yXuQsomo.js → infoDiagram-5YYISTIA-D2jaewWZ.js} +1 -1
- package/web-shell/assets/{ishikawaDiagram-YF4QCWOH-a85JhE6F.js → ishikawaDiagram-YF4QCWOH-BpnNTyi5.js} +1 -1
- package/web-shell/assets/{journeyDiagram-JHISSGLW-Dak_4BVj.js → journeyDiagram-JHISSGLW-CA55GNZF.js} +1 -1
- package/web-shell/assets/{kanban-definition-UN3LZRKU-COA-LuCV.js → kanban-definition-UN3LZRKU-DyiSl_Ts.js} +1 -1
- package/web-shell/assets/{linear-DNAAAQWq.js → linear-B-sN3dkV.js} +1 -1
- package/web-shell/assets/{mermaid.core-7Ff3aIUD.js → mermaid.core-xf9SXDdb.js} +5 -5
- package/web-shell/assets/{mindmap-definition-RKZ34NQL-_rCOIODU.js → mindmap-definition-RKZ34NQL-DXhr2kRk.js} +1 -1
- package/web-shell/assets/{pieDiagram-4H26LBE5-C9QCQ8EW.js → pieDiagram-4H26LBE5-DUXpbfGQ.js} +1 -1
- package/web-shell/assets/{quadrantDiagram-W4KKPZXB-CsaE4FA8.js → quadrantDiagram-W4KKPZXB-NeDQYeWw.js} +1 -1
- package/web-shell/assets/{requirementDiagram-4Y6WPE33-tV78naTg.js → requirementDiagram-4Y6WPE33-BSbFRL1A.js} +1 -1
- package/web-shell/assets/{sankeyDiagram-5OEKKPKP-Dk3Rw7BZ.js → sankeyDiagram-5OEKKPKP-gg27cAaN.js} +1 -1
- package/web-shell/assets/{sequenceDiagram-3UESZ5HK-BGsAWcMX.js → sequenceDiagram-3UESZ5HK-Bi_1TSs4.js} +1 -1
- package/web-shell/assets/{stateDiagram-AJRCARHV-zSxdtdoB.js → stateDiagram-AJRCARHV-CboCCRaf.js} +1 -1
- package/web-shell/assets/stateDiagram-v2-BHNVJYJU-CXRqms6P.js +1 -0
- package/web-shell/assets/{timeline-definition-PNZ67QCA-j0BQh9Go.js → timeline-definition-PNZ67QCA-DrGpF_jn.js} +1 -1
- package/web-shell/assets/{vennDiagram-CIIHVFJN-CRaUFFIu.js → vennDiagram-CIIHVFJN-DCaJU67x.js} +1 -1
- package/web-shell/assets/{wardley-L42UT6IY-D7Py_ofm.js → wardley-L42UT6IY-p0QcW1aP.js} +1 -1
- package/web-shell/assets/{wardleyDiagram-YWT4CUSO-BrDIAZcp.js → wardleyDiagram-YWT4CUSO-DiEVxj1W.js} +1 -1
- package/web-shell/assets/{xychartDiagram-2RQKCTM6-Cecdw-w2.js → xychartDiagram-2RQKCTM6-iQB8nSlA.js} +1 -1
- package/web-shell/index.html +2 -2
- package/chunks/chunk-QFEY6OXH.js +0 -45
- package/chunks/exitPlanMode-LUBAWMZQ.js +0 -390
- package/chunks/nonInteractiveCli-OUGWLHNV.js +0 -129
- package/chunks/todoWrite-HK6B3RHJ.js +0 -358
- package/web-shell/assets/channel-DY0fkcKT.js +0 -1
- package/web-shell/assets/classDiagram-4FO5ZUOK-DBvJ6PZo.js +0 -1
- package/web-shell/assets/classDiagram-v2-Q7XG4LA2-DBvJ6PZo.js +0 -1
- package/web-shell/assets/index-DhXB9nTa.js +0 -1161
- package/web-shell/assets/index-vYHgZ7bL.css +0 -5
- package/web-shell/assets/stateDiagram-v2-BHNVJYJU-vLEEtDpO.js +0 -1
|
@@ -237,17 +237,6 @@ This auth type supports not only OpenAI's official API but also any OpenAI-compa
|
|
|
237
237
|
}
|
|
238
238
|
```
|
|
239
239
|
|
|
240
|
-
For a vision model that can also follow the normal Qwen Code agent policy and use tools, opt in to full-turn image routing with both capabilities:
|
|
241
|
-
|
|
242
|
-
```json
|
|
243
|
-
"capabilities": {
|
|
244
|
-
"vision": true,
|
|
245
|
-
"agent": true
|
|
246
|
-
}
|
|
247
|
-
```
|
|
248
|
-
|
|
249
|
-
When a text-only primary uses that model as its configured vision fallback, the complete image-bearing turn stays on that exact provider, model, and endpoint across tool calls and retries. The next independent turn returns to the primary, and each model request receives only media modalities supported by its target. Omit `agent` (or set it to `false`) to keep the safer Vision Bridge transcription flow.
|
|
250
|
-
|
|
251
240
|
### Local Self-Hosted Models (via OpenAI-compatible API)
|
|
252
241
|
|
|
253
242
|
Most local inference servers (vLLM, Ollama, LM Studio, etc.) provide an OpenAI-compatible API endpoint. Configure them using the `openai` auth type with a local `baseUrl`:
|
|
@@ -139,7 +139,7 @@ Settings are organized into categories. Most settings should be placed within th
|
|
|
139
139
|
| `ui.disableWorkflowKeywordTrigger` | boolean | 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. | `false` |
|
|
140
140
|
| `ui.enableUserFeedback` | boolean | Show an optional feedback dialog after conversations to help improve Qwen performance. | `true` |
|
|
141
141
|
| `ui.compactInline` | boolean | Compact tool display within each group instead of merging across groups. Requires `ui.compactMode` to be enabled. Requires restart. | `false` |
|
|
142
|
-
| `ui.useTerminalBuffer` | boolean | 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. Scroll with `Shift+↑/↓` (line), `PgUp`/`PgDn` (page), `Ctrl+Home/End` (top/bottom), or the mouse wheel. Does not use the host terminal scrollback while enabled;
|
|
142
|
+
| `ui.useTerminalBuffer` | boolean | 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. Scroll with `Shift+↑/↓` (line), `PgUp`/`PgDn` (page), `Ctrl+Home/End` (top/bottom), or the mouse wheel. Does not use the host terminal scrollback while enabled; hold `Shift` (or `Option` on macOS) while dragging for native text selection. | `false` |
|
|
143
143
|
| `ui.hideBuiltinWorktreeIndicator` | boolean | Hide the built-in `⎇ worktree-<branch> (<slug>)` line in the Footer. The worktree state is still passed to custom statusline scripts via the stdin payload. Keep at the default unless your custom statusline renders the worktree itself. | `false` |
|
|
144
144
|
|
|
145
145
|
#### ide
|
|
@@ -175,7 +175,7 @@ Settings are organized into categories. Most settings should be placed within th
|
|
|
175
175
|
| `model.chatCompression.screenshotTriggerThreshold` | number | Tool-returned image count at or above which the screenshot trigger fires (only when `enableScreenshotTrigger`). Compaction resets the count — surviving images are re-embedded as top-level parts, which the trigger doesn't count — so it won't immediately re-fire. Env override: `QWEN_COMPACT_SCREENSHOT_THRESHOLD`. | `20` |
|
|
176
176
|
| `model.skipNextSpeakerCheck` | boolean | Skip the next speaker check. | `true` |
|
|
177
177
|
| `model.skipLoopDetection` | boolean | Disables streaming loop detection checks. Defaults to `true` (loop detection is skipped) to avoid false positives interrupting legitimate workflows. Set to `false` to re-enable streaming loop detection — useful as a guardrail in headless / non-interactive runs where stuck repetition can otherwise waste budget. | `true` |
|
|
178
|
-
| `model.maxToolCallsPerTurn` | integer |
|
|
178
|
+
| `model.maxToolCallsPerTurn` | integer | Hard cap on tool calls within a single turn (one model turn plus its tool-result continuations; blocking Stop-hook continuations such as `/goal` iterations start a fresh budget). Always-on circuit breaker against runaway turns, independent of `model.skipLoopDetection`; the pattern-based loop detectors fire long before this cap, which only bounds total volume. Set to `0` or a negative value to disable the cap. Choosing "Disable loop detection for this session" in the loop-detected dialog also suppresses it for the rest of the session. | `100` |
|
|
179
179
|
| `model.skipStartupContext` | boolean | Skips sending the startup workspace context (environment summary and acknowledgement) at the beginning of each session. Enable this if you prefer to provide context manually or want to save tokens on startup. | `false` |
|
|
180
180
|
| `model.enableOpenAILogging` | boolean | Enables logging of OpenAI API calls for debugging and analysis. When enabled, API requests and responses are logged to JSON files. | `false` |
|
|
181
181
|
| `model.openAILoggingDir` | string | Custom directory path for OpenAI API logs. If not specified, defaults to `logs/openai` in the current working directory. Supports absolute paths, relative paths (resolved from current working directory), and `~` expansion (home directory). | `undefined` |
|
|
@@ -547,13 +547,13 @@ LSP server configuration is done through `.lsp.json` files in your project root
|
|
|
547
547
|
>
|
|
548
548
|
> **Experimental features.** These toggles gate in-development capabilities and may change or be removed in future releases.
|
|
549
549
|
|
|
550
|
-
| Setting | Type | Description
|
|
551
|
-
| -------------------------------------- | ------- |
|
|
552
|
-
| `experimental.cron` | boolean | Enable in-session cron/loop tools (`cron_create`, `cron_list`, `cron_delete`) so the model can create recurring prompts. Can be disabled via the `QWEN_CODE_DISABLE_CRON=1` environment variable. Requires restart.
|
|
553
|
-
| `experimental.cronRecurringMaxAgeDays` | number | Days a recurring cron/loop job lives before auto-expiring (it fires one final time, then is deleted). Set to `0` to disable expiry so jobs run until deleted — useful for long-running daemon deployments. Can be overridden via the `QWEN_CODE_CRON_MAX_AGE_DAYS` environment variable. Requires restart.
|
|
554
|
-
| `experimental.agentTeam` | boolean | Enable agent-team collaboration tools (`team_create`, `task_create`, `task_update`, `send_message`, etc.) for multi-agent coordination. Can also be enabled via `QWEN_CODE_ENABLE_AGENT_TEAM=1`. Requires restart.
|
|
555
|
-
| `experimental.artifact` | boolean | Enable
|
|
556
|
-
| `experimental.emitToolUseSummaries` | boolean | Generate a short LLM-based label after each tool-call batch completes. See [Tool-Use Summaries](../features/tool-use-summaries). Requires a fast model to be configured (`fastModel`); silently skipped otherwise. Can be overridden per-session with `QWEN_CODE_EMIT_TOOL_USE_SUMMARIES=0` or `=1`.
|
|
550
|
+
| Setting | Type | Description | Default |
|
|
551
|
+
| -------------------------------------- | ------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------- |
|
|
552
|
+
| `experimental.cron` | boolean | Enable in-session cron/loop tools (`cron_create`, `cron_list`, `cron_delete`) so the model can create recurring prompts. Can be disabled via the `QWEN_CODE_DISABLE_CRON=1` environment variable. Requires restart. | `true` |
|
|
553
|
+
| `experimental.cronRecurringMaxAgeDays` | number | Days a recurring cron/loop job lives before auto-expiring (it fires one final time, then is deleted). Set to `0` to disable expiry so jobs run until deleted — useful for long-running daemon deployments. Can be overridden via the `QWEN_CODE_CRON_MAX_AGE_DAYS` environment variable. Requires restart. | `7` |
|
|
554
|
+
| `experimental.agentTeam` | boolean | Enable agent-team collaboration tools (`team_create`, `task_create`, `task_update`, `send_message`, etc.) for multi-agent coordination. Can also be enabled via `QWEN_CODE_ENABLE_AGENT_TEAM=1`. Requires restart. | `false` |
|
|
555
|
+
| `experimental.artifact` | boolean | Enable the Artifact tool, letting the model publish a self-contained HTML page and open it in the browser. Interactive, non-SDK sessions only. `QWEN_CODE_ENABLE_ARTIFACT=1` enables metadata-only `record_artifact` for non-SDK daemon sessions and also enables the Artifact tool in interactive sessions; `QWEN_CODE_DISABLE_ARTIFACT=1` disables both. Requires restart. | `false` |
|
|
556
|
+
| `experimental.emitToolUseSummaries` | boolean | Generate a short LLM-based label after each tool-call batch completes. See [Tool-Use Summaries](../features/tool-use-summaries). Requires a fast model to be configured (`fastModel`); silently skipped otherwise. Can be overridden per-session with `QWEN_CODE_EMIT_TOOL_USE_SUMMARIES=0` or `=1`. | `true` |
|
|
557
557
|
|
|
558
558
|
#### mcpServers
|
|
559
559
|
|
|
@@ -92,37 +92,23 @@ Controls how conversation sessions are managed:
|
|
|
92
92
|
Channel memory stores durable context for one chat or thread. Entries have stable
|
|
93
93
|
IDs, so a list response can be used for deterministic follow-up operations.
|
|
94
94
|
|
|
95
|
-
- `记住:默认使用 staging 环境`
|
|
96
|
-
|
|
97
|
-
- To save several separate facts in one request, use a natural phrase routed
|
|
98
|
-
through the classifier. For example:
|
|
99
|
-
`请记住这三条约定:使用 staging;发布前测试;优先中文回复` creates entries
|
|
100
|
-
that you can manage independently. Exact duplicate facts are skipped and
|
|
101
|
-
reported without creating another entry. Requests containing credential-like
|
|
102
|
-
text are rejected; remove secrets and save the non-sensitive facts separately.
|
|
95
|
+
- `记住:默认使用 staging 环境` saves a new entry for the current chat or
|
|
96
|
+
thread.
|
|
103
97
|
- `查看记忆` lists entries and their stable IDs. Use `查看第 2 页记忆` to view
|
|
104
98
|
a later page, `查看记忆 <id>` to view one entry, or a natural filtered
|
|
105
99
|
request such as `只看中文偏好` to list the matching entries.
|
|
106
100
|
- `查看刚才那条记忆`, `把关于 staging 的记忆改成默认使用 production`, and
|
|
107
101
|
`忘掉刚才那条` work when the natural reference resolves to exactly one entry.
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
`确认删除记忆` or `confirm memory removal`, within 60 seconds. Exact-ID
|
|
111
|
-
updates and removals remain immediate and do not need confirmation.
|
|
102
|
+
`把 <id> 改成默认使用 production` updates that entry immediately, and
|
|
103
|
+
`忘掉 <id>` removes it immediately. Neither operation needs confirmation.
|
|
112
104
|
- `清空记忆` starts the clear-all confirmation flow; `确认清空记忆` completes
|
|
113
105
|
it.
|
|
114
106
|
|
|
115
107
|
When a natural inspect, update, or removal request matches multiple entries,
|
|
116
108
|
the bot returns the candidate IDs and previews without changing memory. There
|
|
117
|
-
is no pending selection
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
matched.
|
|
121
|
-
|
|
122
|
-
Pending update, removal, and clear confirmations apply only to the sender and
|
|
123
|
-
chat or thread that created them. A newer clear, natural update, or natural
|
|
124
|
-
removal proposal replaces an older pending one for that sender and target.
|
|
125
|
-
Pending confirmations are discarded when the channel process restarts.
|
|
109
|
+
is no pending selection or confirmation state: retry the request with one exact
|
|
110
|
+
ID, such as `忘掉 m-a31f0d82c7e4`. Exact-ID operations remain the deterministic
|
|
111
|
+
fast path. A natural request with no match reports that no entry matched.
|
|
126
112
|
|
|
127
113
|
The legacy slash aliases `/remember-channel`, `/channel-memory`, and
|
|
128
114
|
`/forget-channel` have been removed. They are no longer channel-memory
|
|
@@ -144,9 +130,10 @@ Memory remains keyed to the current chat or thread. It is not injected into a
|
|
|
144
130
|
`sessionScope: single` session, because that session is shared across the whole
|
|
145
131
|
channel rather than scoped to one target.
|
|
146
132
|
|
|
147
|
-
Channel memory does not automatically learn facts from normal conversation
|
|
148
|
-
|
|
149
|
-
|
|
133
|
+
Channel memory does not automatically learn facts from normal conversation,
|
|
134
|
+
extract multiple entries from one request, or accept `第一个` as confirmation
|
|
135
|
+
for an ambiguous natural reference. Use a clear remember request and an exact
|
|
136
|
+
entry ID when a natural reference is ambiguous.
|
|
150
137
|
|
|
151
138
|
### Token Security
|
|
152
139
|
|
|
@@ -20,34 +20,6 @@ The daemon reads the bearer token from either `--token <value>` on the CLI or th
|
|
|
20
20
|
|
|
21
21
|
One shell-level `export` covers both server boot and SDK client construction (just keep it scoped to the session, per the note above).
|
|
22
22
|
|
|
23
|
-
## Workspace lifecycle and process boundaries
|
|
24
|
-
|
|
25
|
-
One daemon can host several isolated workspace runtimes under the same listener.
|
|
26
|
-
Repeat `--workspace` with absolute directories to create explicit startup
|
|
27
|
-
runtimes; the first is primary. Primary and other explicit startup/static
|
|
28
|
-
runtimes cannot be removed without restarting the process.
|
|
29
|
-
|
|
30
|
-
Additional workspaces can also be registered while the daemon is running
|
|
31
|
-
through `POST /workspaces`. Pass `persist: true` to retain a dynamic secondary
|
|
32
|
-
in the user-level registration store so it is restored on the next start.
|
|
33
|
-
Untrusted registrations remain visible for diagnostics, bounded file reads,
|
|
34
|
-
and declared persisted reads but cannot start ACP. Dynamic and
|
|
35
|
-
persisted-restored secondaries are removable: a normal removal refuses while
|
|
36
|
-
the runtime is busy, while a forced removal requests termination of active
|
|
37
|
-
resources and commits logical removal before the same cwd can be re-added.
|
|
38
|
-
Cleanup is bounded and best-effort after the persistence commit point; failures
|
|
39
|
-
are logged rather than restoring the removed runtime.
|
|
40
|
-
|
|
41
|
-
Runtime isolation covers cwd, environment overlay, filesystem/trust boundary,
|
|
42
|
-
workspace services, bridge, Voice lease state, channel worker, and the ACP/MCP
|
|
43
|
-
resource boundary. Production attempts to preheat the primary ACP child and
|
|
44
|
-
retries on first use after failure; trusted secondaries start theirs on demand,
|
|
45
|
-
and untrusted secondaries do not start ACP.
|
|
46
|
-
Authentication, HTTP rate limits, listener and Voice admission caps,
|
|
47
|
-
total-session admission, metrics, shutdown, and the process fault radius remain
|
|
48
|
-
daemon-global. Run separate daemons when those process-level boundaries must be
|
|
49
|
-
independent.
|
|
50
|
-
|
|
51
23
|
## Linux: systemd user unit
|
|
52
24
|
|
|
53
25
|
> **Find your `qwen` binary first.** The unit file's `ExecStart=` must hold an **absolute path** — service managers don't read your shell's `PATH`. Run `which qwen` to discover it. Common locations: `/usr/local/bin/qwen` (Linuxbrew, manual installs), `~/.nvm/versions/node/vX.Y.Z/bin/qwen` (nvm), `~/.fnm/aliases/default/bin/qwen` (fnm), `~/.volta/bin/qwen` (Volta). Substitute the actual path everywhere the templates below show `/PATH/TO/qwen`.
|
|
@@ -62,9 +34,9 @@ After=network.target
|
|
|
62
34
|
[Service]
|
|
63
35
|
Type=simple
|
|
64
36
|
# Replace with your project; %h expands to $HOME under user units.
|
|
65
|
-
WorkingDirectory=%h/project
|
|
37
|
+
WorkingDirectory=%h/your-project
|
|
66
38
|
# Run `which qwen` to find the absolute path. systemd does NOT read $PATH.
|
|
67
|
-
ExecStart=/PATH/TO/qwen serve --hostname 127.0.0.1 --port 4170
|
|
39
|
+
ExecStart=/PATH/TO/qwen serve --hostname 127.0.0.1 --port 4170
|
|
68
40
|
# Read the bearer token from a chmod 600 file rather than inlining it
|
|
69
41
|
# in the unit. `Environment=` would expose the token in the unit file
|
|
70
42
|
# (typically 644 = world-readable). EnvironmentFile keeps the token in
|
|
@@ -123,14 +95,10 @@ Without `loginctl enable-linger`, the user-level systemd instance shuts down whe
|
|
|
123
95
|
<string>127.0.0.1</string>
|
|
124
96
|
<string>--port</string>
|
|
125
97
|
<string>4170</string>
|
|
126
|
-
<string>--workspace</string>
|
|
127
|
-
<string>/Users/YOUR-USERNAME/project-a</string>
|
|
128
|
-
<string>--workspace</string>
|
|
129
|
-
<string>/Users/YOUR-USERNAME/project-b</string>
|
|
130
98
|
</array>
|
|
131
99
|
<!-- launchd does NOT expand `~` or `$HOME` — use absolute paths. -->
|
|
132
100
|
<key>WorkingDirectory</key>
|
|
133
|
-
<string>/Users/YOUR-USERNAME/project
|
|
101
|
+
<string>/Users/YOUR-USERNAME/your-project</string>
|
|
134
102
|
<key>EnvironmentVariables</key>
|
|
135
103
|
<dict>
|
|
136
104
|
<!-- DO NOT COMMIT this file with a real token. Also chmod 600 the
|
|
@@ -185,7 +153,7 @@ After editing the plist (e.g., rotating the token) you must `unload` then `load`
|
|
|
185
153
|
Assumes `QWEN_SERVER_TOKEN` is already exported in your shell (see the setup section above):
|
|
186
154
|
|
|
187
155
|
```bash
|
|
188
|
-
tmux new -d -s qwen-serve "qwen serve --hostname 127.0.0.1
|
|
156
|
+
tmux new -d -s qwen-serve "cd ~/your-project && qwen serve --hostname 127.0.0.1"
|
|
189
157
|
tmux attach -t qwen-serve # see live logs; Ctrl-b d to detach
|
|
190
158
|
tmux kill-session -t qwen-serve
|
|
191
159
|
```
|
|
@@ -197,14 +165,11 @@ tmux kill-session -t qwen-serve
|
|
|
197
165
|
Assumes `QWEN_SERVER_TOKEN` is already exported in your shell:
|
|
198
166
|
|
|
199
167
|
```bash
|
|
200
|
-
nohup qwen serve --hostname 127.0.0.1
|
|
201
|
-
--workspace /absolute/path/project-a \
|
|
202
|
-
--workspace /absolute/path/project-b \
|
|
203
|
-
> qwen-serve.log 2>&1 &
|
|
168
|
+
nohup bash -c 'cd ~/your-project && qwen serve --hostname 127.0.0.1' > qwen-serve.log 2>&1 &
|
|
204
169
|
echo $! # daemon PID; capture if you want to `kill` cleanly later
|
|
205
170
|
```
|
|
206
171
|
|
|
207
|
-
|
|
172
|
+
The wrapping `bash -c '...'` ensures the daemon binds to `~/your-project` rather than wherever you happened to run the command. Without that `cd`, `qwen serve` defaults to `process.cwd()` and a `POST /session` from a client expecting your project workspace returns `400 workspace_mismatch` — silent foot-gun.
|
|
208
173
|
|
|
209
174
|
OK for one-off "let me run this in the background while I poke at the API" workflows. **Not recommended** for anything beyond a single session — no restart-on-crash, log file grows unbounded, no clean way to find the daemon if you forget the PID. Prefer tmux for interactive supervision or systemd / launchd for anything you want to outlast a reboot.
|
|
210
175
|
|
|
@@ -249,7 +214,7 @@ A daemon **restart** drops all in-memory sessions; clients reconnect and start f
|
|
|
249
214
|
## Out of scope (defers to v0.16.x or later)
|
|
250
215
|
|
|
251
216
|
- **Containerized deployment** — Dockerfile, docker-compose, Kubernetes manifests, nginx + TLS reverse proxy, multi-instance token isolation. Defers to v0.16.x once an enterprise pilot is committed; the doc would otherwise rot from no-one-validating.
|
|
252
|
-
- **Cross-host federation / multi-daemon coordination on one host** —
|
|
217
|
+
- **Cross-host federation / multi-daemon coordination on one host** — `1 daemon = 1 workspace × N sessions` is enforced. Instance-path token keying + stale-token cleanup defer to v0.16.x.
|
|
253
218
|
- **Auto-generated daemon tokens** — alpha is BYO-token. Auto-gen + token-store infrastructure defers to v0.16.x.
|
|
254
219
|
- **Windows native service** (`nssm`, Service Control Manager wrapper) — for now use [WSL2](https://learn.microsoft.com/en-us/windows/wsl/) and follow the systemd section above.
|
|
255
220
|
|
|
@@ -11,7 +11,7 @@ Run Qwen Code as a local HTTP daemon so multiple clients (IDE plugins, web UIs,
|
|
|
11
11
|
## What it gives you
|
|
12
12
|
|
|
13
13
|
- **Built-in Web Shell UI** — `qwen serve` serves the browser-based Web Shell at its root (`http://127.0.0.1:4170/`) out of the box; run `qwen serve --open` to launch it in your browser automatically. It is served on the same origin as the API, so no second port or reverse proxy is needed. Pass `--no-web` for an API-only daemon.
|
|
14
|
-
- **
|
|
14
|
+
- **One agent process, many clients** — under the default `sessionScope: 'single'`, every client connecting to the daemon shares one ACP session. Live cross-client collaboration on the same conversation, the same file diffs, the same permission prompts.
|
|
15
15
|
- **Reconnect-safe streaming** — SSE with `Last-Event-ID` reconnect lets a client drop and pick up exactly where it left off (within the ring's replay window).
|
|
16
16
|
- **Paged persisted transcripts** — `GET /session/:id/transcript` returns the complete active on-disk transcript as replay pages without attaching a client or changing the live SSE replay window.
|
|
17
17
|
- **First-responder permissions** — when the agent asks for permission to run a tool, every connected client sees the request; whichever client answers first wins.
|
|
@@ -51,7 +51,7 @@ The first npm release of `qwen serve` (v0.16-alpha) is intentionally narrow —
|
|
|
51
51
|
- ⏸️ **Prometheus metrics + load test harness** — defers to v0.17 F4 Phase-1 scale instrumentation when 30-50 active sessions becomes a real target.
|
|
52
52
|
- ⏸️ **`--max-body-size` CLI flag** — daemon enforces `express.json({ limit: '10mb' })` by default which comfortably covers text-only prompts (model context windows are well under 10 MiB of chars). Tunable via flag in v0.16.x.
|
|
53
53
|
|
|
54
|
-
For the deeper "what we won't fix in Stage 1" enumeration (single-host session-state mutation model + N
|
|
54
|
+
For the deeper "what we won't fix in Stage 1" enumeration (single-host session-state mutation model + N-parallel-sessions sharing one ACP child), see [Stage 1 scope boundaries](#stage-1-scope-boundaries--what-we-wont-fix-in-stage-15) below.
|
|
55
55
|
|
|
56
56
|
## Quickstart
|
|
57
57
|
|
|
@@ -64,7 +64,7 @@ qwen serve
|
|
|
64
64
|
# → qwen serve: bearer auth disabled (loopback default). Set QWEN_SERVER_TOKEN to enable.
|
|
65
65
|
```
|
|
66
66
|
|
|
67
|
-
The default bind is `127.0.0.1:4170`. Bearer auth is **off** on loopback so local development "just works". The daemon
|
|
67
|
+
The default bind is `127.0.0.1:4170`. Bearer auth is **off** on loopback so local development "just works". The daemon binds to the current working directory; use `--workspace /path/to/dir` to override.
|
|
68
68
|
|
|
69
69
|
**Open the Web Shell UI.** Browse to `http://127.0.0.1:4170/` (or start the daemon with `qwen serve --open` to launch it automatically) for the full browser terminal — chat, diffs, tool calls, and permission prompts. The UI is served at the daemon root on the same origin as the API. The rest of this guide uses raw HTTP so you can script against the API directly.
|
|
70
70
|
|
|
@@ -81,7 +81,7 @@ curl http://127.0.0.1:4170/daemon/status
|
|
|
81
81
|
# → {"v":1,"detail":"summary","status":"ok","runtime":{...}}
|
|
82
82
|
```
|
|
83
83
|
|
|
84
|
-
The `workspaceCwd` field surfaces the
|
|
84
|
+
The `workspaceCwd` field surfaces the bound workspace so clients can pre-flight check + omit `cwd` on `POST /session`.
|
|
85
85
|
The `limits.maxPendingPromptsPerSession` field advertises the active per-session prompt admission cap; `null` means the cap is disabled. `limits.maxTotalSessions` advertises the optional daemon-wide fresh-session cap; `null` means unlimited.
|
|
86
86
|
|
|
87
87
|
### Run channels from the daemon
|
|
@@ -90,7 +90,7 @@ The `limits.maxPendingPromptsPerSession` field advertises the active per-session
|
|
|
90
90
|
# Start one configured channel under qwen serve
|
|
91
91
|
qwen serve --channel telegram
|
|
92
92
|
|
|
93
|
-
# Start several configured channels under daemon-owned
|
|
93
|
+
# Start several configured channels under one daemon-owned worker
|
|
94
94
|
qwen serve --channel telegram --channel feishu
|
|
95
95
|
|
|
96
96
|
# Start all configured channels
|
|
@@ -125,35 +125,11 @@ The daemon also exposes read-only runtime snapshots for client UIs and
|
|
|
125
125
|
operators: `GET /daemon/status`, `GET /workspace/mcp`,
|
|
126
126
|
`GET /workspace/skills`, `GET /workspace/providers`, `GET /workspace/env`,
|
|
127
127
|
`GET /workspace/preflight`,
|
|
128
|
-
`GET /workspace/:id/session-info`,
|
|
129
128
|
`GET /session/:id/status`, `GET /session/:id/context`,
|
|
130
129
|
`GET /session/:id/supported-commands`, and
|
|
131
130
|
`GET /session/:id/tasks`, `GET /session/:id/lsp`, and
|
|
132
131
|
`GET /session/:id/transcript`.
|
|
133
132
|
|
|
134
|
-
`GET /workspace/:id/session-info` (and the plural
|
|
135
|
-
`GET /workspaces/:workspace/session-info` twin) returns aggregate session
|
|
136
|
-
counts for a workspace: persisted `active` / `archived` / `total`, plus the
|
|
137
|
-
current in-memory `live` count when live state is available. Registered
|
|
138
|
-
untrusted secondary workspaces omit `live` because their catalog reads do not
|
|
139
|
-
query the live bridge. The paginated `GET /workspace/:id/sessions` list does
|
|
140
|
-
not include a total, so this is the dedicated surface for “how many sessions
|
|
141
|
-
exist?” — useful when scheduled or recurring tasks leave a large local store.
|
|
142
|
-
|
|
143
|
-
> ⚠️ **Disk scan — do not poll.** This endpoint walks local session JSONL
|
|
144
|
-
> files under the workspace chats directory. Responses always include
|
|
145
|
-
> `expensive: true` and `cost: "disk_scan"`. Call it infrequently (manual
|
|
146
|
-
> refresh, operator tooling, occasional UI load) — never on a tight timer or
|
|
147
|
-
> on every sidebar render. Prefer `GET /workspace/:id/sessions` for browsing
|
|
148
|
-
> pages and `GET /daemon/status` for live in-memory session counts. A response
|
|
149
|
-
> with `truncated: true` means the scan hit its safety limit or could not
|
|
150
|
-
> classify every candidate file, so persisted counts are lower bounds.
|
|
151
|
-
|
|
152
|
-
```bash
|
|
153
|
-
curl http://127.0.0.1:4170/workspace/$(python3 -c "import urllib.parse,os; print(urllib.parse.quote(os.getcwd(), safe=''))")/session-info
|
|
154
|
-
# → {"active":450,"archived":30,"total":480,"live":2,"expensive":true,"cost":"disk_scan"}
|
|
155
|
-
```
|
|
156
|
-
|
|
157
133
|
`GET /session/:id/status` returns the live bridge summary for a single session:
|
|
158
134
|
`sessionId`, `workspaceCwd`, `createdAt`, optional `displayName`, `clientCount`,
|
|
159
135
|
and `hasActivePrompt`. It answers `200` with the summary when the daemon holds a
|
|
@@ -241,7 +217,7 @@ curl -X POST http://127.0.0.1:4170/session \
|
|
|
241
217
|
|
|
242
218
|
`cwd` may be omitted — the route falls back to the daemon's primary workspace. Posting a `cwd` that does not canonicalize to any registered workspace returns `400 workspace_mismatch`.
|
|
243
219
|
|
|
244
|
-
A second client posting to `/session`
|
|
220
|
+
A second client posting to `/session` (any matching `cwd` or none) gets `"attached": true` — they're now sharing the agent.
|
|
245
221
|
|
|
246
222
|
### 4. Subscribe to the event stream (in another terminal first)
|
|
247
223
|
|
|
@@ -351,21 +327,21 @@ Notes:
|
|
|
351
327
|
| `--tls-cert <path>` | — | Path to a PEM certificate file. Serve over **HTTPS** instead of HTTP. Must be paired with `--tls-key` (boot fails if only one is given). Unlocks secure-context browser APIs — voice input (`getUserMedia`), WebRTC — over a LAN IP, which browsers otherwise block on plain `http://`. TLS termination only; no auto-generation / ACME. See [HTTPS / TLS](#https--tls-for-mobile--cross-device-access) below. |
|
|
352
328
|
| `--tls-key <path>` | — | Path to a PEM private key file. Must be paired with `--tls-cert`. |
|
|
353
329
|
| `--max-sessions <n>` | `20` | Cap on concurrent live sessions. New `POST /session` requests that would spawn a fresh child return `503` (with `Retry-After: 5`) when the cap is hit; attaches to existing sessions are NOT counted. Set to `0` to disable. Sized for single-user / small-team usage; raise it if your deployment has the RAM/FD headroom (~30–50 MB per session). |
|
|
354
|
-
| `--max-total-sessions <n>` | derived | Optional non-negative integer daemon-wide cap on fresh session creation across all registered workspace runtimes. It applies to new child sessions, session restore, and branch/fork-created sessions; attaching to an existing live session does not consume a slot. Set to `0` for unlimited. When omitted
|
|
330
|
+
| `--max-total-sessions <n>` | derived | Optional non-negative integer daemon-wide cap on fresh session creation across all registered workspace runtimes. It applies to new child sessions, session restore, and branch/fork-created sessions; attaching to an existing live session does not consume a slot. Set to `0` for unlimited. When omitted in multi-workspace mode, the daemon derives a cap from the per-workspace limit and workspace count. |
|
|
355
331
|
| `--max-pending-prompts-per-session <n>` | `5` | Per-session cap on prompts accepted by `POST /session/:id/prompt` but not yet settled, including queued prompts and the active prompt. The bridge rejects overflow synchronously with `503`, `Retry-After: 5`, and `code: "prompt_queue_full"` before returning a `promptId`. Set to `0` to disable. `branchSession` serializes on the same FIFO but does not count against this prompt cap. |
|
|
356
|
-
| `--workspace <path>` | `process.cwd()` |
|
|
332
|
+
| `--workspace <path>` | `process.cwd()` | Workspace path registered by this daemon. Repeat the flag to host multiple workspaces in one process; the first is primary and remains the default when a request omits `cwd`. Relative paths resolve from the process working directory. Session requests whose canonical `cwd` is not registered return `400 workspace_mismatch`. |
|
|
357
333
|
| `--channel <name\|all>` | — | Experimental daemon-managed channel worker. Repeat the flag to select multiple configured channels, or pass `all` to start every configured channel. `all` cannot be combined with named channels. Selected channel `cwd` values must resolve to a registered workspace; a multi-workspace daemon runs one worker per owning workspace. The worker is owned by `qwen serve`; stop the daemon to stop serve-managed channels. |
|
|
358
334
|
| `--max-connections <n>` | `256` | Listener-level TCP connection cap (`server.maxConnections`). Bounds raw socket count irrespective of session count — slow / phantom SSE clients get rejected at accept time once full. Raise alongside `--max-sessions` if your deployment expects many SSE subscribers per session. |
|
|
359
335
|
| `--event-ring-size <n>` | `8000` | Per-session SSE replay ring depth (#3803 §02 target). Sets the backlog available to `GET /session/:id/events` with `Last-Event-ID: N`. Larger = more reconnect headroom at the cost of a few hundred KB extra RAM per session. SDK clients can additionally request a larger per-subscriber backlog cap on a specific subscription via `?maxQueued=N` (range `[16, 2048]`, default 256). Daemons also emit a non-terminal `slow_client_warning` SSE frame at 75% queue fill so clients can drain / reconnect before getting evicted. Pre-flight `caps.features.slow_client_warning`. |
|
|
360
336
|
| `--compacted-replay-max-bytes <n>` | `4194304` | Per-live-session byte cap for the retained replay events in the bounded snapshot returned by `POST /session/:id/load`. The cap applies to `compactedReplay`; the current in-flight `liveJournal` remains uncapped. Values must be positive safe integers; invalid values fail at boot, and the hard ceiling is 256 MiB. When older retained replay is dropped, the snapshot begins with `history_truncated`. This does not limit the on-disk transcript. |
|
|
361
|
-
| `--mcp-client-budget <n>` | — | Positive integer cap on live MCP clients
|
|
337
|
+
| `--mcp-client-budget <n>` | — | Positive integer cap on live MCP clients **per ACP session** (issue [#4175](https://github.com/QwenLM/qwen-code/issues/4175) PR 14 v1; PR 23 graduates this to per-workspace via the shared MCP pool). Combine with `--mcp-budget-mode`. When unset, no accounting-driven enforcement (but `GET /workspace/mcp` still reports `clientCount`). Distinct from claude-code's `MCP_SERVER_CONNECTION_BATCH_SIZE` which gates startup concurrency, not the total client count. Pre-flight `caps.features.mcp_guardrails`. |
|
|
362
338
|
| `--mcp-budget-mode <m>` | `warn` / `off` | How `--mcp-client-budget` is enforced. `warn` (default when budget set): no refusal, snapshot's `budgets[0].status` flips to `warning` at ≥75% of budget. `enforce`: connects past the cap are refused, per-server cell shows `disabledReason: 'budget'`, deterministic by `mcpServers` declaration order. `off` (default when budget unset): pure observability. Boot rejects `enforce` without a budget. |
|
|
363
|
-
| `--http-bridge` | `true` | Stage 1 mode:
|
|
339
|
+
| `--http-bridge` | `true` | Stage 1 mode: one `qwen --acp` child per registered workspace runtime; sessions targeting that workspace multiplex onto its child via ACP `newSession()`. Stage 2 native in-process becomes available later. |
|
|
364
340
|
| `--allow-origin <pat>` | — | T2.4 ([#4514](https://github.com/QwenLM/qwen-code/issues/4514)). Cross-origin allowlist for browser webui clients. Repeatable. Each value is `*` (any origin — boot refuses if no bearer token is configured; `--require-auth` on loopback is recommended so `/health` and `/demo` are also bearer-gated, since both are pre-auth on loopback by default) or a canonical URL origin (`<scheme>://<host>[:<port>]`, no trailing slash / path / userinfo / query). **Subdomain wildcards (`https://*.example.com`) are intentionally unsupported** — list each subdomain explicitly, or use `*` with a configured token (and `--require-auth` for full hardening). Matched origins receive CORS response headers (`Access-Control-Allow-Origin`, `Vary: Origin`, methods, headers, max-age, and exposed `Retry-After`); unmatched origins still get a 403 with the same envelope as today's wall. `Origin: null` (sandboxed iframes, file:// docs) is always rejected, even under `*`. Pre-flight via `caps.features.allow_origin`. Loopback self-origin hits are unaffected. |
|
|
365
341
|
| `--web` / `--no-web` | `true` | Serve the built Web Shell SPA at the daemon root (`GET /`, `/assets/*`, and SPA deep-link fallback). The static shell is registered **before** the bearer-auth gate — a browser can't attach a token to a `<script>` subresource or an address-bar navigation, the shell carries no secrets, and every API route stays token-gated regardless. On non-loopback binds a one-line stderr warning notes the UI is reachable without auth. Use `--no-web` for an API-only daemon. No effect when the build omits the Web Shell assets (the daemon logs a breadcrumb and runs API-only). |
|
|
366
342
|
| `--open` | `false` | After the listener is up, open the Web Shell in your default browser at the daemon URL (with `#token=` appended as a URL fragment when a token is configured — a fragment is never sent to the server, keeping the token out of access logs and Referer headers). No-op with `--no-web`, or in headless / CI / SSH environments where no browser is available. |
|
|
367
343
|
|
|
368
|
-
> **Sizing the load knobs.** `--max-sessions` is the per-workspace
|
|
344
|
+
> **Sizing the load knobs.** `--max-sessions` is the per-workspace **new-child** cap. `--max-total-sessions`, when set, is the daemon-wide fresh-session cap.
|
|
369
345
|
> Three other layers also limit load — when sizing for a high-concurrency
|
|
370
346
|
> deployment, tune them together:
|
|
371
347
|
>
|
|
@@ -384,20 +360,18 @@ Notes:
|
|
|
384
360
|
> over-capacity client gets a terminal `client_evicted` frame and is
|
|
385
361
|
> closed (one slow consumer can't pin the daemon).
|
|
386
362
|
>
|
|
387
|
-
> These caps interact:
|
|
388
|
-
> `--max-total-sessions` bounds
|
|
389
|
-
>
|
|
390
|
-
>
|
|
391
|
-
>
|
|
392
|
-
>
|
|
393
|
-
>
|
|
394
|
-
>
|
|
395
|
-
> admission layer. Default sizing assumes single-user / small-team load; raise
|
|
396
|
-
> progressively (and watch RSS) for larger deployments.
|
|
363
|
+
> These caps interact: the lower of `--max-sessions` and
|
|
364
|
+
> `--max-total-sessions` bounds fresh sessions in today's single-workspace
|
|
365
|
+
> daemon. `--max-sessions × 64 subscribers × 256 frames`
|
|
366
|
+
> is the worst-case in-flight memory at the EventBus layer, while
|
|
367
|
+
> `--max-sessions × --max-pending-prompts-per-session` bounds accepted
|
|
368
|
+
> prompt work at the admission layer. Default sizing assumes single-user /
|
|
369
|
+
> small-team load; raise progressively (and watch RSS) for multi-tenant
|
|
370
|
+
> deployments.
|
|
397
371
|
|
|
398
372
|
> **MCP client guardrails (issue [#4175](https://github.com/QwenLM/qwen-code/issues/4175) PR 14).** A workspace declaring 30 MCP servers in `mcpServers` will start 30 clients with no upstream cap unless you set one. `--mcp-client-budget=N` caps the live MCP client count; `--mcp-budget-mode={enforce,warn,off}` chooses the behavior. Default is `warn` when a budget is set (snapshot surfaces the warning but no client is refused — useful for measuring real-world fanout before flipping on enforcement). Refused servers under `enforce` mode get `disabledReason: 'budget'` on their per-server cell, and the `budgets[0]` cell shows `status: 'error'` + `errorKind: 'budget_exhausted'`. Slot reservation is by server name and survives reconnects / discovery timeouts — a refused server can't take a slot from a healthy one.
|
|
399
373
|
>
|
|
400
|
-
> **
|
|
374
|
+
> ⚠️ **v1 scope: per-session, not per-workspace.** Each ACP session inside the daemon has its own `Config`/`McpClientManager` (created via `newSessionConfig` per session). The budget caps live MCP clients **per session**, not aggregated across all sessions in the workspace. Snapshot at `GET /workspace/mcp` reflects the bootstrap session's view (the cell carries `scope: 'session'` for honesty). If you run 5 concurrent ACP sessions with `--mcp-client-budget=10`, you may have up to 50 live MCP clients across the daemon — the cap holds per session. **Wave 5 PR 23 (shared MCP pool)** introduces a workspace-scoped manager and graduates this to true per-workspace enforcement.
|
|
401
375
|
>
|
|
402
376
|
> ```sh
|
|
403
377
|
> qwen serve --mcp-client-budget=10 --mcp-budget-mode=warn
|
|
@@ -405,7 +379,7 @@ Notes:
|
|
|
405
379
|
> qwen serve --mcp-client-budget=10 --mcp-budget-mode=enforce
|
|
406
380
|
> ```
|
|
407
381
|
>
|
|
408
|
-
> This is **not** the same as claude-code's `MCP_SERVER_CONNECTION_BATCH_SIZE` (which gates startup concurrency); they
|
|
382
|
+
> This is **not** the same as claude-code's `MCP_SERVER_CONNECTION_BATCH_SIZE` (which gates startup concurrency); they're orthogonal. PR 23 will add a real shared MCP pool (a `scope: 'workspace'` cell in `budgets[]` alongside the per-session cell); PR 14 v1 is the in-process counter + soft enforcement on the existing per-session manager.
|
|
409
383
|
>
|
|
410
384
|
> **Push events (issue [#4175](https://github.com/QwenLM/qwen-code/issues/4175) PR 14b).** SDK clients subscribed to `GET /session/:id/events` receive typed frames when budget thresholds cross — `mcp_budget_warning` (synthetic, fires once per upward 75% crossing with hysteresis re-arm at 37.5%, advertised via `mcp_guardrail_events`) and `mcp_child_refused_batch` (coalesced once per discovery pass under `enforce` mode; length-1 from `readResource` lazy-spawn refusal). The snapshot at `GET /workspace/mcp` is still the source-of-truth for state-after-reconnect; events are change-edges. Useful when dashboarding in real-time without polling.
|
|
411
385
|
|
|
@@ -417,7 +391,7 @@ Notes:
|
|
|
417
391
|
- **Host header allowlist** — on **loopback** binds the daemon checks `Host:` matches `localhost:port` / `127.0.0.1:port` / `[::1]:port` / `host.docker.internal:port` (case-insensitive per RFC 7230 §5.4) to defend against DNS rebinding. **Non-loopback binds (`--hostname 0.0.0.0`) intentionally bypass the Host allowlist** — the operator has chosen the surface area, so the bearer-token gate is the sole authentication layer; reverse proxies / SNI / client cert pinning are the operator's responsibility, not the daemon's. If you need Host-based isolation on a non-loopback bind, terminate TLS + check Host at a front proxy.
|
|
418
392
|
- **CORS denies any browser Origin by default** — returns `403` JSON. Pass **`--allow-origin <pattern>`** (repeatable, T2.4 #4514) to opt specific browser origins through. Each value is either the literal `*` (any origin — boot refuses if no bearer token is configured; `--require-auth` on loopback is recommended for full hardening since `/health` and `/demo` remain pre-auth on loopback by default) or a canonical URL origin (`<scheme>://<host>[:<port>]`, no trailing slash / path / userinfo). Matched origins receive proper CORS response headers (`Access-Control-Allow-Origin: <echoed>`, `Vary: Origin`, plus standard methods / headers / max-age and exposed `Retry-After`); unmatched origins still get a 403 with the same envelope as the default wall. `caps.features.allow_origin` is advertised conditionally so SDK / webui clients can pre-flight whether the daemon honors cross-origin hits before issuing them. Example: `qwen serve --allow-origin http://localhost:3000 --allow-origin http://localhost:5173`. Loopback self-origin hits (e.g. the `/demo` page) are unaffected — a separate Origin-strip shim handles them regardless of `--allow-origin`. **Browser webuis without `--allow-origin` configured** still fall back to the same Stage 1 options as before: package as a native shell (Electron/Tauri) so no `Origin` header is sent, or front the daemon with a same-origin reverse proxy.
|
|
419
393
|
- **Chrome extension browser automation is separate from framing.** `qwen serve --allow-origin chrome-extension://<id>` lets the extension frame the Web Shell and connect to the daemon. Console/network/screenshot/click tools require an external CDP MCP adapter command: `QWEN_CDP_MCP_COMMAND=/path/to/cdp-mcp-adapter qwen serve --allow-origin chrome-extension://<id>`. The main CLI package does not bundle a browser automation adapter; clients can check `caps.features.includes('browser_automation_mcp')` before presenting those tools as available.
|
|
420
|
-
- **
|
|
394
|
+
- **Spawned `qwen --acp` child inherits the daemon's environment** with one explicit scrub: `QWEN_SERVER_TOKEN` is removed before the child starts (the daemon's own bearer; the agent doesn't need it). Everything else — `OPENAI_API_KEY` / `ANTHROPIC_API_KEY` / `QWEN_*` / `DASHSCOPE_API_KEY` / your custom `modelProviders[].envKey` / etc. — passes through, because the agent legitimately needs those to authenticate to the LLM. **This is intentional, not a sandbox.** The agent runs as the same UID with shell-tool access, so anything in `~/.bashrc` / `~/.aws/credentials` / `~/.npmrc` is reachable by prompt injection regardless. The env passthrough is not the security boundary; the user-as-trust-root is. Don't run `qwen serve` under an identity that has env-resident credentials you wouldn't trust the agent with.
|
|
421
395
|
- **Per-subscriber bounded SSE queues** — a slow client that overflows its queue gets a `client_evicted` terminal frame and is closed; one stuck consumer can't pin the daemon.
|
|
422
396
|
- **Per-session prompt admission cap** — defaults to 5 accepted-but-unsettled prompts per session. A buggy client cannot enqueue unbounded prompt promises or temporary SSE waits for one session.
|
|
423
397
|
- **Graceful shutdown** — SIGINT/SIGTERM drain the agent children before closing the listener (10s deadline per child).
|
|
@@ -461,15 +435,15 @@ Both flags accept a positive integer in milliseconds; `0`, `NaN`, non-integer, o
|
|
|
461
435
|
|
|
462
436
|
## Multi-session & multi-workspace deployment
|
|
463
437
|
|
|
464
|
-
Pass `--workspace` more than once to register several non-overlapping workspaces in one `qwen serve` process. The first path is primary. Each registered workspace owns an isolated runtime boundary, while the daemon-wide listener, authentication policy, and total-session limit are shared.
|
|
438
|
+
Pass `--workspace` more than once to register several non-overlapping workspaces in one `qwen serve` process. The first path is primary. Each registered workspace owns an isolated runtime boundary, while the daemon-wide listener, authentication policy, and total-session limit are shared. Trusted workspaces start their own `qwen --acp` child on demand. Requests may select a registered workspace by canonical `cwd`; requests that omit `cwd` use the primary workspace. Use one daemon per user or security principal; workspace trust is an execution gate, not an ACL.
|
|
465
439
|
|
|
466
|
-
An untrusted secondary workspace is visible in Web Shell as `untrusted` and `read-only`. It can be expanded to inspect the persisted session catalog, but it cannot yet be selected or opened in Web Shell, resumed, used to create sessions, or fully exported. The REST API
|
|
440
|
+
An untrusted secondary workspace is visible in Web Shell as `untrusted` and `read-only`. It can be expanded to inspect the persisted session catalog, but it cannot yet be selected or opened in Web Shell, resumed, used to create sessions, or fully exported. The REST API exposes its persisted session-group catalog and, when `workspace_persisted_transcript` is advertised, its active persisted transcript through the bounded workspace-qualified pager. These reads do not include live runtime state or start an ACP child. Full workspace-qualified export requires a trusted workspace and the separate `workspace_session_export` capability. Trust the workspace and restart the daemon before using execution, mutation, or export features. An untrusted primary remains disabled in Web Shell.
|
|
467
441
|
|
|
468
442
|
Use separate daemon processes when you need a smaller fault or security boundary, independent bearer tokens, quotas, audit boundaries, operating-system isolation, or independent resource supervision. Multi-workspace mode is intended for one operator hosting several repos; it is not a multi-tenant isolation boundary. A single daemon token authorizes every route the daemon exposes, including the allowed read-only catalog for all registered workspaces.
|
|
469
443
|
|
|
470
444
|
> **Subscribe BEFORE posting `modelServiceId` on attach.** When a client `POST /session` with a `modelServiceId` and the workspace already has a session running a different model, the daemon issues an internal `setSessionModel` call — failures are NOT propagated as an HTTP error (the session stays operational on its current model). The visible failure signal is a `model_switch_failed` event on the session's SSE stream. If you call `POST /session` and only THEN open `GET /session/:id/events`, you'll miss the failure event and silently keep talking to the wrong model. Open the SSE stream first, or pass `Last-Event-ID: 0` on subscribe to replay the ring's oldest available event.
|
|
471
445
|
|
|
472
|
-
To handle multiple **users
|
|
446
|
+
To handle multiple **users** (each with their own quota, audit log, sandbox) or to scale beyond one process's reach (cold-start budget, FD count, RSS), spawn one daemon per workspace per user behind an external orchestrator. That orchestrator (multi-tenancy / OIDC / Quota / Audit / k8s) is **out of scope** for the qwen-code project — see issue [#3803](https://github.com/QwenLM/qwen-code/issues/3803) "External Reference Architecture" for the design pointers.
|
|
473
447
|
|
|
474
448
|
## Loading and resuming a persisted session
|
|
475
449
|
|
|
@@ -516,7 +490,7 @@ For a registered workspace, use `client.workspaceById(workspaceId).getSessionTra
|
|
|
516
490
|
|
|
517
491
|
For a full attachment from a trusted registered workspace, pre-flight `workspace_session_export` and call `client.workspaceById(workspaceId).exportSession(sessionId, { format: 'html' })` or the raw `/workspaces/:workspace/session/:id/export` route. Do not infer support from `session_export` or `workspace_qualified_rest_core`: older daemons can advertise both while retaining primary-only export. The current Web Shell export action remains primary-only; use the SDK or REST route for another workspace.
|
|
518
492
|
|
|
519
|
-
For an archived attachment, pre-flight `workspace_archived_session_export` and call `client.workspaceById(workspaceId).exportArchivedSession(sessionId, { format: 'html' })` or `/workspaces/:workspace/session/:id/archive/export`. This path reads archived storage in place and returns `409 session_not_archived` for an active-only id; it does not unarchive the session.
|
|
493
|
+
For an archived attachment, pre-flight `workspace_archived_session_export` and call `client.workspaceById(workspaceId).exportArchivedSession(sessionId, { format: 'html' })` or `/workspaces/:workspace/session/:id/archive/export`. This path reads archived storage in place and returns `409 session_not_archived` for an active-only id; it does not unarchive the session.
|
|
520
494
|
|
|
521
495
|
`limit` counts active chat records, not emitted replay frames; one record can produce several `session_update` events. The first response freezes the JSONL snapshot size and returns `nextCursor` while `hasMore` is true. Later pages ignore appends after page 1, but return `409` if the file is deleted, truncated, replaced, archived, or otherwise conflicts with the frozen cursor. Very large snapshots return `413 transcript_too_large` before indexing so the daemon does not scan unbounded transcript files on the request path.
|
|
522
496
|
|
|
@@ -596,26 +570,26 @@ In this mode, TUI is a **"super-client"** — it observes the same agent convers
|
|
|
596
570
|
|
|
597
571
|
(B) is the more ambitious answer but locks Stage 1.5 into a substantially larger wire surface that must also pass cleanly through the planned in-process refactor. We'd rather walk the smaller scope honestly. The session-state-event taxonomy work — enumerating which TUI flows are local-only by design vs. could plausibly graduate to wire under a future opt-in (B)-flavor extension — moves to [#3803](https://github.com/QwenLM/qwen-code/issues/3803), not Stage 1.5 code.
|
|
598
572
|
|
|
599
|
-
### N parallel sessions share one `qwen --acp` child
|
|
573
|
+
### N parallel sessions share one `qwen --acp` child
|
|
600
574
|
|
|
601
|
-
Multiple sessions on the same
|
|
575
|
+
Multiple sessions on the same workspace **share one `qwen --acp` child process** via the agent's native multi-session support (`packages/cli/src/acp-integration/acpAgent.ts:194: private sessions: Map<string, Session>`). The bridge calls `connection.newSession({cwd, mcpServers})` for each session — the agent stores them in its sessions map and demultiplexes per-call sessionId.
|
|
602
576
|
|
|
603
577
|
Concrete cost at N=5 sessions on the same workspace:
|
|
604
578
|
|
|
605
|
-
| Resource | Per session
|
|
606
|
-
| ------------------------------------ |
|
|
607
|
-
| Daemon Node process | one
|
|
608
|
-
| `qwen --acp` child | shared
|
|
609
|
-
| MCP server children |
|
|
610
|
-
| `FileReadCache` (in-child heap) | shared
|
|
611
|
-
| `CLAUDE.md` / hierarchy memory parse | shared
|
|
612
|
-
| OAuth refresh-token state | shared
|
|
613
|
-
| Auto-memory learned facts | shared
|
|
614
|
-
| Cold start | first only
|
|
579
|
+
| Resource | Per session | At N=5 |
|
|
580
|
+
| ------------------------------------ | ----------- | ---------------------------- |
|
|
581
|
+
| Daemon Node process | one | **30–50 MB** (one daemon) |
|
|
582
|
+
| `qwen --acp` child | shared | **60–100 MB** (one child) |
|
|
583
|
+
| MCP server children | per-session | 3×N if configs differ |
|
|
584
|
+
| `FileReadCache` (in-child heap) | shared | parsed once |
|
|
585
|
+
| `CLAUDE.md` / hierarchy memory parse | shared | parsed once |
|
|
586
|
+
| OAuth refresh-token state | shared | **one refresh path** |
|
|
587
|
+
| Auto-memory learned facts | shared | one knowledge base per child |
|
|
588
|
+
| Cold start | first only | <200 ms after first session |
|
|
615
589
|
|
|
616
|
-
|
|
590
|
+
The bridge keeps **one channel per daemon** (one daemon per workspace, per §02). The channel stays alive while at least one session is live; the last `killSession` (or a channel-level crash) kills the child.
|
|
617
591
|
|
|
618
|
-
**MCP server children**
|
|
592
|
+
**MCP server children** are still per-session today — each session's config can specify different servers, so they're independently spawned. Stage 1.5 follow-up: refcount MCP server children by `(workspace, config-hash)` so identical configs share. Not in scope for this PR.
|
|
619
593
|
|
|
620
594
|
**Peer agents (Cursor / Continue / Claude Code / OpenCode / Gemini CLI) all do single-process multi-session.** qwen-code matches them at the agent layer; the Stage 1 bridge in this PR makes the same architecture visible over HTTP.
|
|
621
595
|
|
|
@@ -81,7 +81,7 @@ Active only when `ui.useTerminalBuffer` is enabled (Settings → UI → Virtuali
|
|
|
81
81
|
| `Ctrl+End` | Jump to the bottom (and re-engage live auto-follow). |
|
|
82
82
|
| **Mouse wheel** | Scroll history (3 lines per tick). |
|
|
83
83
|
|
|
84
|
-
When `ui.useTerminalBuffer` is on, the terminal forwards mouse events to qwen-code so the wheel can drive the in-app viewport. As a side effect, native click-and-drag text selection is consumed by the program
|
|
84
|
+
When `ui.useTerminalBuffer` is on, the terminal forwards mouse events to qwen-code so the wheel can drive the in-app viewport. As a side effect, **native click-and-drag text selection is consumed by the program** — hold `Shift` (or `Option` on macOS Terminal / iTerm) while dragging to bypass mouse capture and select text the usual way.
|
|
85
85
|
|
|
86
86
|
### tmux trackpad scrolling
|
|
87
87
|
|