@qwen-code/qwen-code 0.19.7 → 0.19.8-nightly.20260711.0ef3a76bd
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/SKILL.md +1 -0
- package/bundled/qc-helper/docs/configuration/auth.md +40 -55
- package/bundled/qc-helper/docs/configuration/model-providers.md +224 -218
- package/bundled/qc-helper/docs/configuration/settings.md +60 -51
- package/bundled/qc-helper/docs/features/_meta.ts +1 -0
- package/bundled/qc-helper/docs/features/channels/dingtalk.md +12 -2
- package/bundled/qc-helper/docs/features/channels/overview.md +86 -12
- package/bundled/qc-helper/docs/features/channels/plugins.md +1 -0
- package/bundled/qc-helper/docs/features/channels/wecom.md +6 -0
- package/bundled/qc-helper/docs/features/code-review.md +29 -5
- package/bundled/qc-helper/docs/features/commands.md +39 -38
- package/bundled/qc-helper/docs/features/computer-use.md +77 -0
- package/bundled/qc-helper/docs/features/hooks.md +36 -34
- package/bundled/qc-helper/docs/features/memory.md +1 -1
- package/bundled/qc-helper/docs/features/tool-use-summaries.md +19 -23
- package/bundled/qc-helper/docs/integration-vscode.md +2 -2
- package/bundled/qc-helper/docs/qwen-serve.md +40 -16
- package/bundled/qc-helper/docs/reference/keyboard-shortcuts.md +1 -1
- package/bundled/review/DESIGN.md +93 -22
- package/bundled/review/SKILL.md +300 -87
- package/chunks/MaxSizedBox-4CX4IQWE.js +70 -0
- package/chunks/{StandaloneSessionPicker-APISIRJZ.js → StandaloneSessionPicker-TYUSSNJ6.js} +79 -77
- package/chunks/{acpAgent-MMOSC2YA.js → acpAgent-5HJEXOPR.js} +978 -205
- package/chunks/agent-PZF5IUUB.js +68 -0
- package/chunks/agent-headless-L4N7V2SG.js +62 -0
- package/chunks/{anthropicContentGenerator-AKYPSZIV.js → anthropicContentGenerator-L73TO3DB.js} +214 -31
- package/chunks/{artifact-tool-D233YJFH.js → artifact-tool-U7IRTLIV.js} +5 -5
- package/chunks/{askUserQuestion-MKL5ZTMC.js → askUserQuestion-2DLUUZZG.js} +6 -6
- package/chunks/bridge-OJL6LD42.js +78 -0
- package/chunks/{ca-HCMB2I54.js → ca-WTQOU43B.js} +91 -6
- package/chunks/{channel-worker-supervisor-CRCVYQIE.js → channel-worker-supervisor-LF66HCVT.js} +137 -13
- package/chunks/{chunk-QQDPRDVW.js → chunk-2HRYPZT5.js} +1 -1
- package/chunks/{chunk-JD33UY7E.js → chunk-2K56LTWA.js} +18 -12
- package/chunks/{chunk-XISQIIT7.js → chunk-2LV4JUNO.js} +4 -4
- package/chunks/chunk-2UU6DW5P.js +296 -0
- package/chunks/{chunk-2PSWA5ID.js → chunk-2X56YUSZ.js} +1 -1
- package/chunks/chunk-2ZQOQZIS.js +71 -0
- package/chunks/{chunk-Q6OEPOOC.js → chunk-3AMVHPEQ.js} +2 -2
- package/chunks/{chunk-4RQIB5UZ.js → chunk-3BTRNWWD.js} +1 -35
- package/chunks/{chunk-SD4N3QJ2.js → chunk-3QZ6GY6A.js} +66 -22
- package/chunks/{chunk-DNV4FSN5.js → chunk-3SJSQXNS.js} +24 -7
- package/chunks/{chunk-VQWAZSXN.js → chunk-3XATDZNP.js} +64 -42
- package/chunks/{chunk-LEIJKM7Q.js → chunk-3ZAIGBCP.js} +2 -2
- package/chunks/{chunk-2NPS2UDH.js → chunk-43JB7OF4.js} +905 -139
- package/chunks/chunk-43XJTCHT.js +444 -0
- package/chunks/{chunk-CNSF3WO5.js → chunk-47HXCVGG.js} +6 -6
- package/chunks/{chunk-VEB3NYSO.js → chunk-4GORAUCN.js} +8 -3
- package/chunks/{chunk-55ZMG67I.js → chunk-4LD5APFL.js} +4 -4
- package/chunks/{chunk-TLWA7TPR.js → chunk-4QLQE5WJ.js} +2 -2
- package/chunks/{chunk-UIDXQNMV.js → chunk-4WGODMVR.js} +1 -1
- package/chunks/{chunk-E6US47KI.js → chunk-4WKAA4KS.js} +1 -1
- package/chunks/{chunk-H6EMK6QK.js → chunk-4ZVWQHEQ.js} +11913 -5207
- package/chunks/{chunk-HW4N45EE.js → chunk-524RQP4Q.js} +29 -8
- package/chunks/{chunk-3GONHQOA.js → chunk-57OAHC2Y.js} +1 -1
- package/chunks/{chunk-B4U2M63D.js → chunk-5DOUQHL3.js} +2104 -14
- package/chunks/{chunk-JZSY4WP3.js → chunk-5DTTYNB7.js} +1 -1
- package/chunks/{chunk-A4BMJM77.js → chunk-5O2XNYP6.js} +1 -0
- package/chunks/{chunk-P7I3D7FM.js → chunk-5OIXALQ5.js} +2 -2
- package/chunks/chunk-5SFOMFG4.js +61 -0
- package/chunks/{chunk-QYNNN7BK.js → chunk-5XUCZNSY.js} +2 -2
- package/chunks/{chunk-FRFFNYUY.js → chunk-65F3I3PM.js} +5 -5
- package/chunks/{chunk-GO6LNQXT.js → chunk-6AM5D4ME.js} +1 -1
- package/chunks/{chunk-BWVQN2HV.js → chunk-6CR73ZIK.js} +7 -7
- package/chunks/{chunk-BJBWRCSK.js → chunk-6H67XLET.js} +1 -1
- package/chunks/{chunk-G7ZG7CA6.js → chunk-6RLROE6G.js} +9 -8
- package/chunks/{chunk-TBAZMRW3.js → chunk-6WH2WCSW.js} +2 -2
- package/chunks/{chunk-ZERZSAZL.js → chunk-75DOP5OR.js} +2 -2
- package/chunks/{chunk-FTFXDZYI.js → chunk-7KZNLIAI.js} +47 -31
- package/chunks/{chunk-HJ3GX3Y3.js → chunk-7PW5CTVR.js} +32 -18
- package/chunks/{chunk-NQKLOAVJ.js → chunk-7R3CZA3Q.js} +2 -2
- package/chunks/{chunk-SAIILPQ3.js → chunk-7X7TEQNX.js} +5 -3
- package/chunks/{chunk-IRJWHZWU.js → chunk-A4UCTEB5.js} +2 -2
- package/chunks/{chunk-XSAFNANZ.js → chunk-AHUEU3BP.js} +14 -14
- package/chunks/{chunk-X3YGAX7V.js → chunk-B5ORX3FG.js} +4 -2
- package/chunks/{chunk-ZN5T4BHI.js → chunk-BPZALHVR.js} +2 -2
- package/chunks/{chunk-IJRL2S3D.js → chunk-BZIFY5TF.js} +6 -6
- package/chunks/{chunk-AJBEAUIS.js → chunk-BZP7HZTK.js} +7 -6
- package/chunks/{chunk-OFEVLU4C.js → chunk-CARU2RR2.js} +1 -1
- package/chunks/{chunk-MIG5SQNL.js → chunk-CBKR4Q5K.js} +8 -8
- package/chunks/chunk-CC2ITGCF.js +176 -0
- package/chunks/{chunk-FV7425LN.js → chunk-CEA3E3JB.js} +18 -5
- package/chunks/{chunk-GD236YPV.js → chunk-CNOF2IZT.js} +3 -3
- package/chunks/chunk-COH43MRJ.js +34 -0
- package/chunks/{chunk-E5A7LHNN.js → chunk-CPBF7KYF.js} +1 -1
- package/chunks/{chunk-UWCTAVOD.js → chunk-CR3C7WXL.js} +1 -1
- package/chunks/{chunk-5P5XGNYH.js → chunk-CU3L64TP.js} +1 -1
- package/chunks/{chunk-IWKSG2AR.js → chunk-CXAOG665.js} +1 -1
- package/chunks/{chunk-MZ7BABX3.js → chunk-DC5Z3DU6.js} +2 -2
- package/chunks/{chunk-64WXLC72.js → chunk-DJ2GSRLV.js} +1 -1
- package/chunks/{chunk-4ZJIPVPB.js → chunk-DMN532MJ.js} +11 -11
- package/chunks/{chunk-QN5NZ3UQ.js → chunk-DMTGGOSA.js} +2 -2
- package/chunks/{chunk-KHDZHZMH.js → chunk-E5Z2AVNV.js} +1 -1
- package/chunks/chunk-F4PFZ7WP.js +239 -0
- package/chunks/{chunk-ME3YSROJ.js → chunk-FBSBT7JS.js} +25 -13
- package/chunks/{chunk-5IFG2VC4.js → chunk-FCMNKVXT.js} +1 -1
- package/chunks/{chunk-YDSYD7UI.js → chunk-FD3DA3B4.js} +3 -3
- package/chunks/{chunk-GL5TV7B5.js → chunk-GL22M2DU.js} +23 -7
- package/chunks/{chunk-AFOLR6YL.js → chunk-GQ4ZEYMI.js} +102 -16
- package/chunks/chunk-GRC5HGPI.js +13 -0
- package/chunks/{chunk-WMPVYQ4P.js → chunk-GWHYEOV2.js} +2 -2
- package/chunks/chunk-GZRTYZ2R.js +292 -0
- package/chunks/{chunk-A2X3KLC3.js → chunk-GZXCZHCT.js} +3 -3
- package/chunks/{chunk-K5PGHDBN.js → chunk-H6XPXXMH.js} +1 -1
- package/chunks/{chunk-24XYI5EI.js → chunk-HACY3IUM.js} +6 -6
- package/chunks/{chunk-DGHJTO2K.js → chunk-HIHQ3TWC.js} +38 -9
- package/chunks/{chunk-KYMBIKIW.js → chunk-HTHUX2T5.js} +1 -1
- package/chunks/{chunk-LQ7TMOCE.js → chunk-HTO4JFDZ.js} +1 -1
- package/chunks/{chunk-B34OV7K2.js → chunk-IAVB3HM3.js} +15 -2
- package/chunks/{chunk-4UCTWSTP.js → chunk-IDJEZU7C.js} +2 -2
- package/chunks/chunk-IEBIKN6A.js +38 -0
- package/chunks/{chunk-PRDFOIZF.js → chunk-IJEO2QSK.js} +73 -30
- package/chunks/{chunk-MJI36HER.js → chunk-IT6LIIVK.js} +2 -2
- package/chunks/{chunk-FEJ2FZ3U.js → chunk-J3JA76CF.js} +2 -2
- package/chunks/{chunk-4CGOWHKM.js → chunk-J4MOFEOK.js} +2 -2
- package/chunks/{chunk-22MNNNXE.js → chunk-J4TSQ5EQ.js} +3 -2
- package/chunks/{chunk-22IFUCVR.js → chunk-JESCGQM3.js} +1 -1
- package/chunks/{chunk-CCGOFQV3.js → chunk-JSG7ZSBC.js} +8 -7
- package/chunks/{chunk-X265R5C6.js → chunk-K434OPES.js} +5 -5
- package/chunks/{chunk-RFIL6REB.js → chunk-KHUQZZJ6.js} +185 -45
- package/chunks/{chunk-X2XNQQJI.js → chunk-KKNMOEZH.js} +4 -4
- package/chunks/{chunk-6BN7NZ5Z.js → chunk-KL23O5Z5.js} +7 -9
- package/chunks/{chunk-MRO43B25.js → chunk-KW7NOTN6.js} +1 -1
- package/chunks/{chunk-6IAWPIQ6.js → chunk-L36P3NBD.js} +3 -3
- package/chunks/{chunk-54YZOP7P.js → chunk-L573Z2QZ.js} +11894 -5190
- package/chunks/{chunk-STAZRKOT.js → chunk-L6GPKRH7.js} +132 -464
- package/chunks/{chunk-QAKFYMJI.js → chunk-LD5OWZ5E.js} +155 -19
- package/chunks/{chunk-E7KNELIZ.js → chunk-LK7KUE55.js} +2 -2
- package/chunks/{chunk-W7EEOR7D.js → chunk-LUODI7WD.js} +5 -5
- package/chunks/{chunk-CSOTA7KX.js → chunk-LXNEJD34.js} +2 -2
- package/chunks/{chunk-HLKG6CZN.js → chunk-M5GB774H.js} +1 -1
- package/chunks/{chunk-3B26UVCX.js → chunk-MC23XKFE.js} +39 -10
- package/chunks/{chunk-XPJH4PKR.js → chunk-MPEMTBUZ.js} +2 -2
- package/chunks/{chunk-GMOWQOQQ.js → chunk-MUCWGT7O.js} +248 -55
- package/chunks/{chunk-PC4ZXHU2.js → chunk-MXL5UP4X.js} +3 -3
- package/chunks/{chunk-PUQLHSQ2.js → chunk-N2FGNKOE.js} +10 -12
- package/chunks/{chunk-332PWN27.js → chunk-NHDKSOEY.js} +1 -1
- package/chunks/{chunk-65CFHNSE.js → chunk-NKHV3OHY.js} +62 -62
- package/chunks/{chunk-P7M4GA57.js → chunk-OCPBI7J5.js} +1 -1
- package/chunks/{chunk-42XGFRJS.js → chunk-ORBGV7NJ.js} +5 -5
- package/chunks/{chunk-ALZ75L2X.js → chunk-OZJLTZKP.js} +415 -379
- package/chunks/chunk-P3X7RYUQ.js +468 -0
- package/chunks/{chunk-AGHU5VRK.js → chunk-PZL23GTM.js} +1 -1
- package/chunks/{chunk-FXGLL2HL.js → chunk-Q7YDKOGY.js} +1 -1
- package/chunks/{chunk-LGFAYIKX.js → chunk-QHTIBUWB.js} +1 -1
- package/chunks/{chunk-AIY5UTFI.js → chunk-QK2JET6S.js} +934 -2881
- package/chunks/{chunk-P3MYAZBY.js → chunk-QMGX2KO2.js} +1 -1
- package/chunks/{chunk-QZF5U5KA.js → chunk-QNTUV3PP.js} +11 -11
- package/chunks/chunk-QRUIS3DN.js +47 -0
- package/chunks/{chunk-ITOGNELQ.js → chunk-QSWDGAIC.js} +2 -2
- package/chunks/{chunk-7JGISHHZ.js → chunk-QUJIDGIW.js} +15 -157
- package/chunks/{chunk-GHOTR7HL.js → chunk-QUPXZXLV.js} +1 -1
- package/chunks/{chunk-TDGF35EN.js → chunk-QYMQSECS.js} +3 -3
- package/chunks/{chunk-QYUE6W3T.js → chunk-RDJSGHZE.js} +1 -1
- package/chunks/{chunk-TLRYABYP.js → chunk-RKUWKYED.js} +1 -1
- package/chunks/{chunk-ADZG46HR.js → chunk-RRNR5U2U.js} +2 -2
- package/chunks/{chunk-4O344UEE.js → chunk-RW7CXRUM.js} +39 -19
- package/chunks/{chunk-RNBYOUGV.js → chunk-RXFQM6FQ.js} +1 -1
- package/chunks/{chunk-QXPI7FEC.js → chunk-S3TWXGKA.js} +4 -4
- package/chunks/{chunk-44NROQYV.js → chunk-SJHDYNHF.js} +4 -4
- package/chunks/{chunk-MLZQVCF3.js → chunk-SZRN75WQ.js} +1 -1
- package/chunks/{chunk-7RYW5LQV.js → chunk-T2NCM2ET.js} +2 -2
- package/chunks/{chunk-Z2Z3GUXZ.js → chunk-TB6UDU4T.js} +1 -1
- package/chunks/{chunk-SUAKHHK7.js → chunk-TDEXZKMT.js} +3 -3
- package/chunks/{chunk-EXPMGZZV.js → chunk-TEGEBB2I.js} +1 -1
- package/chunks/{chunk-JF7DFPQ5.js → chunk-TJCEUA3S.js} +15 -3
- package/chunks/{chunk-MX2YXRER.js → chunk-TJTKSVIV.js} +1 -1
- package/chunks/{chunk-H6BD2ELD.js → chunk-TYAMGABM.js} +2 -2
- package/chunks/{chunk-CISB3IQF.js → chunk-U3VSCGEJ.js} +100 -15
- package/chunks/{chunk-VRDMOSQS.js → chunk-UDXSXLER.js} +2 -2
- package/chunks/{chunk-AHNJGLFV.js → chunk-UOZWKTQG.js} +3 -3
- package/chunks/{chunk-CJCKDMWO.js → chunk-URK542T4.js} +1 -1
- package/chunks/{chunk-YEGT55NW.js → chunk-UUVCHN62.js} +5697 -2983
- package/chunks/{chunk-AHNXYU4O.js → chunk-V6WHFGST.js} +1 -1
- package/chunks/{chunk-U4FRHCLA.js → chunk-VAUPLGDZ.js} +2370 -364
- package/chunks/{chunk-ZTQ26VBE.js → chunk-VDDGDZMA.js} +1 -1
- package/chunks/{chunk-OMX7CUOE.js → chunk-VGC4I5JJ.js} +1 -1
- package/chunks/{chunk-IUE7YWC3.js → chunk-VIUKEWUL.js} +8 -8
- package/chunks/{chunk-43W4YSO6.js → chunk-VJNBRRZG.js} +2 -2
- package/chunks/{chunk-I4A4K72C.js → chunk-VL7GBUQA.js} +1309 -703
- package/chunks/{chunk-VZ7HY3YU.js → chunk-W2UIXQDL.js} +3 -3
- package/chunks/{chunk-ER3BKOLB.js → chunk-W7FJ3N32.js} +1 -1
- package/chunks/{chunk-3HX5LZ6R.js → chunk-WBL3FJEU.js} +2 -2
- package/chunks/{chunk-RYFZCUJO.js → chunk-WCUK2QXK.js} +17 -9
- package/chunks/{chunk-OWVIH4HY.js → chunk-WFHJDT4S.js} +421 -114
- package/chunks/{chunk-BMFMJINR.js → chunk-WL4L7EZH.js} +1 -1
- package/chunks/{chunk-NZEGNV4V.js → chunk-WPHYXKFL.js} +8 -51
- package/chunks/chunk-X4NKFGFJ.js +81 -0
- package/chunks/{chunk-3RW4AZJV.js → chunk-XFSFZCM5.js} +1 -1
- package/chunks/{chunk-2MPVVENX.js → chunk-XU457J4V.js} +2 -2
- package/chunks/{chunk-SIUQ3YYX.js → chunk-XVNQMZ2I.js} +1 -1
- package/chunks/{chunk-SYCJMSIJ.js → chunk-XWRJCPHC.js} +1 -1
- package/chunks/chunk-YAF2MQZ6.js +19 -0
- package/chunks/chunk-YF4RZPJP.js +168 -0
- package/chunks/{chunk-4KJ2BPIT.js → chunk-YLV3XXFB.js} +4 -40
- package/chunks/{chunk-BR4QREVK.js → chunk-YQ3U5MUC.js} +1 -1
- package/chunks/{chunk-Y6Z2O3WR.js → chunk-YUZI3WAC.js} +1 -1
- package/chunks/{chunk-II73RK2S.js → chunk-YVEDCAUE.js} +1 -1
- package/chunks/{chunk-SKAZWEV5.js → chunk-ZJE7NSL3.js} +1 -1
- package/chunks/{chunk-7LTB54MK.js → chunk-ZLXQN2QS.js} +2 -2
- package/chunks/cli-entry-path-4VJ3Y6T2.js +10 -0
- package/chunks/{computer-use-X4KUU3NM.js → computer-use-2ZTYDNN4.js} +50 -50
- package/chunks/config-utils-ZITWCFBY.js +23 -0
- package/chunks/contextCommand-6L4XCZZT.js +68 -0
- package/chunks/create-sub-session-INX7TXJI.js +207 -0
- package/chunks/create-sub-session-VZIOFS6N.js +375 -0
- package/chunks/{cron-create-JSXEIPVS.js → cron-create-AMBKGZX6.js} +9 -9
- package/chunks/{cron-delete-OIFVEPGU.js → cron-delete-LRU4OJYG.js} +7 -7
- package/chunks/{cron-list-D4ZX4IOE.js → cron-list-WVP2JXER.js} +14 -10
- package/chunks/{daemon-F3I2RXP6.js → daemon-QEDXW7ER.js} +554 -4
- package/chunks/daemon-status-provider-7XJZ3HPS.js +76 -0
- package/chunks/{de-PLLFDERI.js → de-XHBEYUMG.js} +91 -6
- package/chunks/{devtools-FM6GJPYG.js → devtools-4QFYJT6U.js} +3 -3
- package/chunks/{dist-573HNSVO.js → dist-EFXT7A3G.js} +12 -11
- package/chunks/{dist-PKFTNUHO.js → dist-KCRX7TGL.js} +529 -79
- package/chunks/{dist-OT7G2WNM.js → dist-MFEAEUV4.js} +1011 -197
- package/chunks/{dist-63IS3ZMI.js → dist-PZV5RKM6.js} +4 -4
- package/chunks/{dist-FQ5WRJPL.js → dist-QY3THJM5.js} +5 -5
- package/chunks/{dist-IMFFBMAT.js → dist-UJOR6KEQ.js} +32 -15
- package/chunks/{dist-YTRKAVUB.js → dist-ZWZB4CCN.js} +10 -10
- package/chunks/earlyInputCapture-TTWXFIWO.js +69 -0
- package/chunks/{edit-WZTZDGCI.js → edit-6UWFRZKY.js} +51 -51
- package/chunks/{en-GA2RMXY5.js → en-TKHUFIK7.js} +100 -6
- package/chunks/{enter-worktree-DEPXBJEL.js → enter-worktree-LS7SGAII.js} +50 -50
- package/chunks/{enterPlanMode-JFGXZHHJ.js → enterPlanMode-TSZYLEEP.js} +66 -51
- package/chunks/environment-XCVLHOXG.js +88 -0
- package/chunks/errors-KIANLUTC.js +75 -0
- package/chunks/{exit-worktree-Y7XBK3VV.js → exit-worktree-2QV6UJSW.js} +50 -50
- package/chunks/{exitPlanMode-WQQER65F.js → exitPlanMode-GVZG3F6S.js} +51 -51
- package/chunks/{fast-path-W2U6XDNO.js → fast-path-GNFZAMQK.js} +16 -7
- package/chunks/{fast-path-settings-RCRXH2JG.js → fast-path-settings-5QZE64HR.js} +6 -5
- package/chunks/{fileFromPath-IBEHA3CO.js → fileFromPath-UDALK7FM.js} +3 -3
- package/chunks/{fr-GWNQG4XH.js → fr-TBF6JZNE.js} +91 -6
- package/chunks/{gemini-T4WX6SRW.js → gemini-N3H6OLHR.js} +501 -197
- package/chunks/{geminiContentGenerator-MKQACXEJ.js → geminiContentGenerator-4C257R2N.js} +9 -9
- package/chunks/{getMachineId-bsd-4CASPIU4.js → getMachineId-bsd-EXV7SWPA.js} +3 -3
- package/chunks/{getMachineId-darwin-HPQPEMZR.js → getMachineId-darwin-IUOMFXU3.js} +3 -3
- package/chunks/{getMachineId-linux-AUARKYHL.js → getMachineId-linux-K7XJYFHL.js} +2 -2
- package/chunks/{getMachineId-unsupported-S32ZDA2T.js → getMachineId-unsupported-EA5FDRJ6.js} +2 -2
- package/chunks/{getMachineId-win-4EFLHYIJ.js → getMachineId-win-KBA6RAI5.js} +3 -3
- package/chunks/{glob-4KKSHZDF.js → glob-QTUI5I3Y.js} +95 -74
- package/chunks/{grep-GHRYXSLZ.js → grep-Z5MGLRCG.js} +50 -50
- package/chunks/handleAutoUpdate-FFF3CZGP.js +69 -0
- package/chunks/i18n-QQAJT7A2.js +84 -0
- package/chunks/initializer-LGQGC7ZT.js +71 -0
- package/chunks/installationInfo-4FEZGXIN.js +65 -0
- package/chunks/{ja-2HOTA47X.js → ja-CDXUA2H3.js} +92 -7
- package/chunks/{keychain-token-storage-UJV5XP4Z.js → keychain-token-storage-VKUBNCP4.js} +4 -4
- package/chunks/{kittyProtocolDetector-OJRCJLIU.js → kittyProtocolDetector-HCNNKJTI.js} +2 -2
- package/chunks/list-KIVFF3BB.js +74 -0
- package/chunks/loadedSettingsAdapter-EOD3RVZS.js +68 -0
- package/chunks/{loop-wakeup-OHWEMFP5.js → loop-wakeup-WX7BLWWP.js} +10 -10
- package/chunks/{lowlight-FYAAUU5J.js → lowlight-J2OZNPCA.js} +1 -1
- package/chunks/{ls-L654WOAT.js → ls-ZB2BDP6T.js} +6 -6
- package/chunks/{lsp-M35KE54Y.js → lsp-V5DN4YUJ.js} +6 -8
- package/chunks/mcp-D2P46DB4.js +68 -0
- package/chunks/{monitor-464OY2UE.js → monitor-4ZWVOL6J.js} +50 -50
- package/chunks/{multipart-parser-AJ4WASWR.js → multipart-parser-AWZKXSQN.js} +3 -3
- package/chunks/nonInteractiveCli-NAHUR2X6.js +124 -0
- package/chunks/{notebook-edit-BLJYCNDO.js → notebook-edit-RCQ2D2IE.js} +51 -51
- package/chunks/openaiContentGenerator-U655T5QC.js +56 -0
- package/chunks/pidfile-O3OO2TCP.js +73 -0
- package/chunks/{pt-2THT3YHA.js → pt-6IR2VRQY.js} +91 -6
- package/chunks/{qwenContentGenerator-SUUHZ6CR.js → qwenContentGenerator-ABGWCRS6.js} +52 -52
- package/chunks/{qwenOAuth2-ZV7YLMND.js → qwenOAuth2-PJQ27GSO.js} +9 -9
- package/chunks/read-file-X7M4GUPY.js +30 -0
- package/chunks/{read-mcp-resource-CNQHBSJM.js → read-mcp-resource-SM5DA4RG.js} +5 -5
- package/chunks/{read-package-up-ER5OJUGP.js → read-package-up-TCM6I7S2.js} +2 -2
- package/chunks/{record-artifact-PP3LKHTS.js → record-artifact-4XKZX3UW.js} +7 -7
- package/chunks/ripGrep-KMPJJK5J.js +60 -0
- package/chunks/{ru-Z67YJHBG.js → ru-DBTHQA3F.js} +91 -6
- package/chunks/{run-qwen-serve-5OKADVLE.js → run-qwen-serve-O6CMPURG.js} +958 -184
- package/chunks/runtime-THE2YO27.js +94 -0
- package/chunks/{scheduler-ZRW3AIDU.js → scheduler-XXEA6EFC.js} +51 -51
- package/chunks/{send-message-OA6MV4EU.js → send-message-F36YD6KQ.js} +7 -7
- package/chunks/serve-W6F44JQQ.js +74 -0
- package/chunks/{server-M372UENX.js → server-SAEKMY52.js} +4340 -829
- package/chunks/{session-F5KSNP2N.js → session-4GGYGOWY.js} +162 -117
- package/chunks/settings-PYEU5HVD.js +118 -0
- package/chunks/shell-JGQECKTH.js +68 -0
- package/chunks/{skill-A2TL2NPC.js → skill-XPP3C2DS.js} +24 -24
- package/chunks/spawnChannel-62QKQBQN.js +70 -0
- package/chunks/{src-XKZMNHXS.js → src-2656Q6SN.js} +126 -52
- package/chunks/{src-7XL4G4DC.js → src-VLI3TKEY.js} +4 -4
- package/chunks/standalone-update-X7AZVG5R.js +79 -0
- package/chunks/{startInteractiveUI-REKSTOHU.js → startInteractiveUI-DXR4K6T5.js} +2277 -1834
- package/chunks/stdioHelpers-UMY72QRP.js +14 -0
- package/chunks/{syntheticOutput-F7LSDCAR.js → syntheticOutput-34H7LGA7.js} +5 -5
- package/chunks/task-create-FRGOEIKY.js +22 -0
- package/chunks/{task-list-TGGJX7EA.js → task-list-OW766BXZ.js} +10 -10
- package/chunks/{task-stop-W7NGTMYQ.js → task-stop-SIR5SNBN.js} +4 -4
- package/chunks/{task-update-5WUG5MVQ.js → task-update-YRA532MT.js} +12 -12
- package/chunks/{team-create-TBQVWAG7.js → team-create-3EHCUCKA.js} +50 -50
- package/chunks/{team-delete-UHQQ2QQX.js → team-delete-6OSECUZC.js} +10 -10
- package/chunks/{team-plan-approval-FZPGUSHJ.js → team-plan-approval-KZ7NZIMX.js} +50 -50
- package/chunks/theme-manager-J7ANV4JR.js +64 -0
- package/chunks/{todoWrite-C4ZAMSR3.js → todoWrite-YDMFEV4G.js} +6 -6
- package/chunks/{tool-search-FDQ3SLNJ.js → tool-search-2XLGWG4H.js} +24 -24
- package/chunks/total-session-admission-EQIHUOL6.js +72 -0
- package/chunks/trustedFolders-BEX5334Z.js +79 -0
- package/chunks/{types-QX5C3CHJ.js → types-JNKGKUJT.js} +2 -2
- package/chunks/updateCheck-NKPJGJCR.js +69 -0
- package/chunks/updateEventEmitter-5MKC7BLQ.js +10 -0
- package/chunks/validateNonInterActiveAuth-6DGGGE4A.js +190 -0
- package/chunks/{version-CEDD6ZZR.js → version-FRVWU5ZU.js} +3 -3
- package/chunks/{web-fetch-UYGMOHW4.js → web-fetch-PAAEQKBW.js} +8 -8
- package/chunks/{workflow-CQQ4J4I6.js → workflow-LWFMECLD.js} +51 -51
- package/chunks/workspace-providers-status-NE7QWEYS.js +71 -0
- package/chunks/workspace-registry-RCWAP57G.js +76 -0
- package/chunks/workspace-service-4QUMVGBX.js +80 -0
- package/chunks/workspace-skills-status-L34BYRSY.js +70 -0
- package/chunks/{write-file-VWGY7AJP.js → write-file-WXO4M5FN.js} +51 -51
- package/chunks/{yargs-B64EYTCF.js → yargs-6H2AUULL.js} +2 -2
- package/chunks/{zh-7XVVRTDH.js → zh-LJP47LX7.js} +101 -7
- package/chunks/{zh-TW-DPH554OG.js → zh-TW-5ISMO3VJ.js} +101 -7
- package/cli.js +18 -17
- package/locales/ca.js +117 -7
- package/locales/de.js +116 -7
- package/locales/en.js +133 -7
- package/locales/fr.js +117 -7
- package/locales/ja.js +119 -7
- package/locales/pt.js +116 -7
- package/locales/ru.js +116 -7
- package/locales/zh-TW.js +130 -7
- package/locales/zh.js +130 -7
- package/package.json +3 -6
- package/web-shell/assets/{arc-C7hjaa7g.js → arc-BRDEF7h6.js} +1 -1
- package/web-shell/assets/{architectureDiagram-3BPJPVTR-pm9KtMZY.js → architectureDiagram-3BPJPVTR-Cxnk4Ygq.js} +1 -1
- package/web-shell/assets/{blockDiagram-GPEHLZMM-CApElKsU.js → blockDiagram-GPEHLZMM-BZrtayqw.js} +1 -1
- package/web-shell/assets/{c4Diagram-AAUBKEIU-BeDe0-9N.js → c4Diagram-AAUBKEIU-Djmbi1yB.js} +1 -1
- package/web-shell/assets/channel-C9zfQCn-.js +1 -0
- package/web-shell/assets/{chunk-2J33WTMH-DJEkmLdl.js → chunk-2J33WTMH-Cdokj-0U.js} +1 -1
- package/web-shell/assets/{chunk-4BX2VUAB-DXX8gdpe.js → chunk-4BX2VUAB-BI9Hx57o.js} +1 -1
- package/web-shell/assets/{chunk-55IACEB6-DORmk1kQ.js → chunk-55IACEB6-Cb17cEzj.js} +1 -1
- package/web-shell/assets/{chunk-727SXJPM-DWmO4mCZ.js → chunk-727SXJPM-DY1Fc0h_.js} +1 -1
- package/web-shell/assets/{chunk-AQP2D5EJ-C7LRaYw2.js → chunk-AQP2D5EJ-B10G71Hd.js} +1 -1
- package/web-shell/assets/{chunk-FMBD7UC4-CCdXgyC5.js → chunk-FMBD7UC4-BP6OoB6Z.js} +1 -1
- package/web-shell/assets/{chunk-ND2GUHAM-SyL_xvWc.js → chunk-ND2GUHAM-CWIIwUFC.js} +1 -1
- package/web-shell/assets/{chunk-QZHKN3VN-BzOuCKxa.js → chunk-QZHKN3VN-DMrqCfGO.js} +1 -1
- package/web-shell/assets/classDiagram-4FO5ZUOK-BVNaf5JM.js +1 -0
- package/web-shell/assets/classDiagram-v2-Q7XG4LA2-BVNaf5JM.js +1 -0
- package/web-shell/assets/{cose-bilkent-S5V4N54A-BMPWyDKc.js → cose-bilkent-S5V4N54A-CF-mpkgI.js} +1 -1
- package/web-shell/assets/{dagre-BM42HDAG-B_GnPWik.js → dagre-BM42HDAG-DfsRFsg7.js} +1 -1
- package/web-shell/assets/{diagram-2AECGRRQ-fJqMc3S_.js → diagram-2AECGRRQ-DBNwf6Dy.js} +1 -1
- package/web-shell/assets/{diagram-5GNKFQAL-ximV3O9D.js → diagram-5GNKFQAL-CD1TqV1j.js} +1 -1
- package/web-shell/assets/{diagram-KO2AKTUF-2vVyjzqw.js → diagram-KO2AKTUF-DkWlaLI2.js} +1 -1
- package/web-shell/assets/{diagram-LMA3HP47-Dckcx1sT.js → diagram-LMA3HP47-ClrAcxwF.js} +1 -1
- package/web-shell/assets/{diagram-OG6HWLK6-BAVv47ga.js → diagram-OG6HWLK6-Dr6dnPLw.js} +1 -1
- package/web-shell/assets/{erDiagram-TEJ5UH35-Bn3nf5J0.js → erDiagram-TEJ5UH35-Dm_vj18R.js} +1 -1
- package/web-shell/assets/{flowDiagram-I6XJVG4X-BIbGF9JT.js → flowDiagram-I6XJVG4X-lp3cMdZu.js} +1 -1
- package/web-shell/assets/{ganttDiagram-6RSMTGT7-CDbhb3AH.js → ganttDiagram-6RSMTGT7-AtdCWZya.js} +3 -3
- package/web-shell/assets/{gitGraphDiagram-PVQCEYII-CbKG1a_w.js → gitGraphDiagram-PVQCEYII-BARTJafF.js} +1 -1
- package/web-shell/assets/index-BkNgyfu7.js +3 -0
- package/web-shell/assets/index-CihmnZZR.js +784 -0
- package/web-shell/assets/index-zfdXhnwH.css +5 -0
- package/web-shell/assets/{infoDiagram-5YYISTIA-DUFxTy8m.js → infoDiagram-5YYISTIA-CxbJ54_f.js} +1 -1
- package/web-shell/assets/{ishikawaDiagram-YF4QCWOH-D69hrBG8.js → ishikawaDiagram-YF4QCWOH-hFoO0HQA.js} +1 -1
- package/web-shell/assets/{journeyDiagram-JHISSGLW-DsvEw51Y.js → journeyDiagram-JHISSGLW-Bo6cKnLe.js} +1 -1
- package/web-shell/assets/{kanban-definition-UN3LZRKU-lFFjZEYd.js → kanban-definition-UN3LZRKU-DIFrH-XL.js} +1 -1
- package/web-shell/assets/{linear-Bo5bWzvo.js → linear-Bjv_GjDd.js} +1 -1
- package/web-shell/assets/{mermaid.core-C_HKMJx5.js → mermaid.core-C_OiqQEP.js} +5 -5
- package/web-shell/assets/{mindmap-definition-RKZ34NQL-DlV-NUTs.js → mindmap-definition-RKZ34NQL-B79Gk8aB.js} +1 -1
- package/web-shell/assets/{pieDiagram-4H26LBE5-B2e6FH2a.js → pieDiagram-4H26LBE5-B_P0gIHr.js} +1 -1
- package/web-shell/assets/{quadrantDiagram-W4KKPZXB-D75kSk-b.js → quadrantDiagram-W4KKPZXB-BC7F0ohb.js} +1 -1
- package/web-shell/assets/{requirementDiagram-4Y6WPE33-CQ6xakhM.js → requirementDiagram-4Y6WPE33-BrA5_NSW.js} +1 -1
- package/web-shell/assets/{sankeyDiagram-5OEKKPKP-J_XTKrcY.js → sankeyDiagram-5OEKKPKP-Q2iqrNI_.js} +1 -1
- package/web-shell/assets/{sequenceDiagram-3UESZ5HK-D7jTcBtf.js → sequenceDiagram-3UESZ5HK-6vdIWz4H.js} +1 -1
- package/web-shell/assets/{stateDiagram-AJRCARHV-CrlM_OT3.js → stateDiagram-AJRCARHV-DMzXNKcB.js} +1 -1
- package/web-shell/assets/stateDiagram-v2-BHNVJYJU-BWFH0OCO.js +1 -0
- package/web-shell/assets/{timeline-definition-PNZ67QCA-5oZi76Ef.js → timeline-definition-PNZ67QCA-BykEawmS.js} +1 -1
- package/web-shell/assets/{vennDiagram-CIIHVFJN-DO5YptPK.js → vennDiagram-CIIHVFJN-B24KD2X1.js} +1 -1
- package/web-shell/assets/{wardley-L42UT6IY-4t1eJDvM.js → wardley-L42UT6IY-C761G_tA.js} +1 -1
- package/web-shell/assets/{wardleyDiagram-YWT4CUSO-DKZc661S.js → wardleyDiagram-YWT4CUSO-CfwGJYqw.js} +1 -1
- package/web-shell/assets/{xychartDiagram-2RQKCTM6-su9hp08G.js → xychartDiagram-2RQKCTM6-BDu5AZ2D.js} +1 -1
- package/web-shell/index.html +16 -4
- package/chunks/MaxSizedBox-XY5LIO5O.js +0 -71
- package/chunks/agent-TKIBLLQT.js +0 -68
- package/chunks/agent-headless-VLX4C7KX.js +0 -62
- package/chunks/bridge-BCU73DZO.js +0 -77
- package/chunks/chunk-6SHO7WFF.js +0 -79
- package/chunks/chunk-IFEWJXFS.js +0 -37
- package/chunks/chunk-S6RLAIUR.js +0 -23
- package/chunks/contextCommand-LAYJZF7H.js +0 -68
- package/chunks/daemon-status-provider-XSAFKVDS.js +0 -74
- package/chunks/earlyInputCapture-AERYYGXE.js +0 -69
- package/chunks/errors-GMZL4QV7.js +0 -75
- package/chunks/handleAutoUpdate-HN7ZOSXY.js +0 -65
- package/chunks/initializer-SRN3IM42.js +0 -69
- package/chunks/list-INTVXUPA.js +0 -71
- package/chunks/loadedSettingsAdapter-QVHHQPGM.js +0 -66
- package/chunks/mcp-7V4T46RG.js +0 -66
- package/chunks/nonInteractiveCli-LJEEODU2.js +0 -116
- package/chunks/openaiContentGenerator-PFF6HYB2.js +0 -56
- package/chunks/pidfile-LIBUENIV.js +0 -73
- package/chunks/read-file-L7TBQVX7.js +0 -30
- package/chunks/ripGrep-CKKX3GRB.js +0 -60
- package/chunks/serve-36WS32W6.js +0 -71
- package/chunks/settings-WFLNS3WT.js +0 -114
- package/chunks/shell-DEDSWN56.js +0 -68
- package/chunks/spawnChannel-RQHUWTCL.js +0 -70
- package/chunks/task-create-RPFZNB4B.js +0 -22
- package/chunks/theme-manager-DQZVNSOC.js +0 -64
- package/chunks/trustedFolders-Z475EIHL.js +0 -79
- package/chunks/updateCheck-E2ZX7RL7.js +0 -65
- package/chunks/validateNonInterActiveAuth-O4ECBHQL.js +0 -181
- package/chunks/workspace-providers-status-MVYXMYDG.js +0 -68
- package/chunks/workspace-service-OMBR6ZLG.js +0 -78
- package/chunks/workspace-skills-status-IMMQ7CAY.js +0 -68
- package/node_modules/@qwen-code/audio-capture/dist/index.d.ts +0 -35
- package/node_modules/@qwen-code/audio-capture/dist/index.js +0 -48
- package/node_modules/@qwen-code/audio-capture/dist/index.js.map +0 -1
- package/node_modules/@qwen-code/audio-capture/dist/platform.d.ts +0 -7
- package/node_modules/@qwen-code/audio-capture/dist/platform.js +0 -18
- package/node_modules/@qwen-code/audio-capture/dist/platform.js.map +0 -1
- package/node_modules/@qwen-code/audio-capture/node_modules/node-gyp-build/LICENSE +0 -21
- package/node_modules/@qwen-code/audio-capture/node_modules/node-gyp-build/README.md +0 -58
- package/node_modules/@qwen-code/audio-capture/node_modules/node-gyp-build/SECURITY.md +0 -5
- package/node_modules/@qwen-code/audio-capture/node_modules/node-gyp-build/bin.js +0 -84
- package/node_modules/@qwen-code/audio-capture/node_modules/node-gyp-build/build-test.js +0 -19
- package/node_modules/@qwen-code/audio-capture/node_modules/node-gyp-build/index.js +0 -6
- package/node_modules/@qwen-code/audio-capture/node_modules/node-gyp-build/node-gyp-build.js +0 -207
- package/node_modules/@qwen-code/audio-capture/node_modules/node-gyp-build/optional.js +0 -7
- package/node_modules/@qwen-code/audio-capture/node_modules/node-gyp-build/package.json +0 -43
- package/node_modules/@qwen-code/audio-capture/package.json +0 -30
- package/node_modules/@qwen-code/audio-capture/prebuilds/darwin-arm64/@qwen-code+audio-capture.node +0 -0
- package/node_modules/@qwen-code/audio-capture/prebuilds/darwin-x64/@qwen-code+audio-capture.node +0 -0
- package/node_modules/@qwen-code/audio-capture/prebuilds/linux-arm64/@qwen-code+audio-capture.node +0 -0
- package/node_modules/@qwen-code/audio-capture/prebuilds/linux-x64/@qwen-code+audio-capture.node +0 -0
- package/node_modules/@qwen-code/audio-capture/prebuilds/win32-x64/@qwen-code+audio-capture.node +0 -0
- package/web-shell/assets/channel-BNf1dW-j.js +0 -1
- package/web-shell/assets/classDiagram-4FO5ZUOK-uOIGlzgi.js +0 -1
- package/web-shell/assets/classDiagram-v2-Q7XG4LA2-uOIGlzgi.js +0 -1
- package/web-shell/assets/index-CnE2ur2L.js +0 -779
- package/web-shell/assets/index-hSKXrlSE.css +0 -5
- package/web-shell/assets/stateDiagram-v2-BHNVJYJU-Bs4TK_84.js +0 -1
|
@@ -13,9 +13,10 @@ Run Qwen Code as a local HTTP daemon so multiple clients (IDE plugins, web UIs,
|
|
|
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
|
+
- **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.
|
|
16
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.
|
|
17
18
|
- **One daemon, one workspace** — each `qwen serve` process binds to exactly one workspace at boot (per [#3803](https://github.com/QwenLM/qwen-code/issues/3803) §02). Multi-workspace deployments run one daemon per workspace on separate ports (or behind an orchestrator).
|
|
18
|
-
- **Experimental daemon-managed channels** — `qwen serve --channel <name>` starts a channel worker owned by the daemon lifecycle. The worker is a separate process, connects back to the daemon through the SDK, and reports its state in `GET /daemon/status`.
|
|
19
|
+
- **Experimental daemon-managed channels** — `qwen serve --channel <name>` starts a channel worker owned by the daemon lifecycle. The worker is a separate process, connects back to the daemon through the SDK, and reports its state in `GET /daemon/status`. After editing channel settings, reload it in place with `POST /workspace/channel/reload` (or `qwen channel reload`) — no full daemon restart needed.
|
|
19
20
|
- **Remote runtime control** ([#4175](https://github.com/QwenLM/qwen-code/issues/4175) PR 17) — change a session's approval mode (`POST /session/:id/approval-mode`), toggle a tool per workspace (`POST /workspace/tools/:name/enable`), scaffold an empty `QWEN.md` (`POST /workspace/init`, mechanical only — does NOT call the model; for AI-fill, follow up with `POST /session/:id/prompt`), restart a single MCP server with a budget pre-check (`POST /workspace/mcp/:server/restart`), or add/remove MCP servers at runtime without a daemon restart (`POST /workspace/mcp/servers`, `DELETE /workspace/mcp/servers/:name`). All strict-gated — configure `--token` first.
|
|
20
21
|
- **Session recap** ([#4175](https://github.com/QwenLM/qwen-code/issues/4175) follow-up) — fetch a one-sentence "where did I leave off" summary of an active session (`POST /session/:id/recap`). Wraps core's `generateSessionRecap` as a side-query against the fast model; pollutes neither the main chat history nor the SSE stream. Non-strict gate (same posture as `/prompt`); SDK helper `client.recapSession(sessionId)`.
|
|
21
22
|
- **Known limit — token-cost amplification:** the route is a pure-cost endpoint (each call is an LLM side-query, no state benefit) and the daemon has no per-route rate limit in v1. On a no-token loopback default a buggy or malicious local client can spam it to burn tokens. Configure `--token` (and optionally `--require-auth`) on shared dev hosts before exposing the daemon.
|
|
@@ -81,7 +82,7 @@ curl http://127.0.0.1:4170/daemon/status
|
|
|
81
82
|
```
|
|
82
83
|
|
|
83
84
|
The `workspaceCwd` field surfaces the bound workspace so clients can pre-flight check + omit `cwd` on `POST /session`.
|
|
84
|
-
The `limits.maxPendingPromptsPerSession` field advertises the active per-session prompt admission cap; `null` means the cap is disabled.
|
|
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.
|
|
85
86
|
|
|
86
87
|
### Run channels from the daemon
|
|
87
88
|
|
|
@@ -96,7 +97,9 @@ qwen serve --channel telegram --channel feishu
|
|
|
96
97
|
qwen serve --channel all
|
|
97
98
|
```
|
|
98
99
|
|
|
99
|
-
This mode is experimental and daemon-managed. It does not replace the standalone `qwen channel start` command: standalone channels still use the ACP-backed `AcpBridge` service. With `qwen serve --channel`, the daemon launches one channel worker process after the HTTP runtime is ready. If the worker
|
|
100
|
+
This mode is experimental and daemon-managed. It does not replace the standalone `qwen channel start` command: standalone channels still use the ACP-backed `AcpBridge` service. With `qwen serve --channel`, the daemon launches one channel worker process after the HTTP runtime is ready. If the worker crashes after startup, the daemon keeps running, relaunches it under a bounded restart policy, and reports its state (including `channel_worker_exited` warnings) in `GET /daemon/status`.
|
|
101
|
+
|
|
102
|
+
The daemon reads each channel's settings (tokens, `proxy`, per-channel `model`) from `settings.json` once, when the worker starts. To apply changes without restarting the whole daemon, call `POST /workspace/channel/reload` (strict-gated; SDK `client.reloadChannelWorker()`, or `qwen channel reload`). The daemon stops and relaunches the channel worker, which re-reads `settings.json`; every selected channel briefly disconnects and reconnects, and persisted threads are restored from disk. The route is advertised as the `channel_reload` capability only when the daemon was started with `--channel`. Adding a brand-new channel name to a `--channel <names>` selection still requires a daemon restart (or use `--channel all`, which picks up newly-configured channels on reload).
|
|
100
103
|
|
|
101
104
|
The daemon is bound to one workspace, so every selected channel's `cwd` must resolve to the daemon workspace. `--channel all` cannot be combined with named channels.
|
|
102
105
|
|
|
@@ -106,7 +109,8 @@ operators: `GET /daemon/status`, `GET /workspace/mcp`,
|
|
|
106
109
|
`GET /workspace/preflight`,
|
|
107
110
|
`GET /session/:id/status`, `GET /session/:id/context`,
|
|
108
111
|
`GET /session/:id/supported-commands`, and
|
|
109
|
-
`GET /session/:id/tasks`,
|
|
112
|
+
`GET /session/:id/tasks`, `GET /session/:id/lsp`, and
|
|
113
|
+
`GET /session/:id/transcript`.
|
|
110
114
|
|
|
111
115
|
`GET /session/:id/status` returns the live bridge summary for a single session:
|
|
112
116
|
`sessionId`, `workspaceCwd`, `createdAt`, optional `displayName`, `clientCount`,
|
|
@@ -303,11 +307,13 @@ Notes:
|
|
|
303
307
|
| `--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. |
|
|
304
308
|
| `--tls-key <path>` | — | Path to a PEM private key file. Must be paired with `--tls-cert`. |
|
|
305
309
|
| `--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). |
|
|
310
|
+
| `--max-total-sessions <n>` | unlimited | Optional non-negative integer daemon-wide cap on fresh session creation across the runtime. 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` or omit the flag for unlimited. This is a guardrail for future multi-workspace sessions and does not enable multi-workspace serving by itself. |
|
|
306
311
|
| `--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. |
|
|
307
312
|
| `--workspace <path>` | `process.cwd()` | Absolute workspace path this daemon binds to (per [#3803](https://github.com/QwenLM/qwen-code/issues/3803) §02 — 1 daemon = 1 workspace). `POST /session` requests with a mismatched `cwd` return `400 workspace_mismatch`. For multi-workspace deployments, run one `qwen serve` per workspace on separate ports. |
|
|
308
313
|
| `--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 the daemon workspace. The worker is owned by `qwen serve`; stop the daemon to stop serve-managed channels. |
|
|
309
314
|
| `--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. |
|
|
310
315
|
| `--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`. |
|
|
316
|
+
| `--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. |
|
|
311
317
|
| `--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`. |
|
|
312
318
|
| `--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. |
|
|
313
319
|
| `--http-bridge` | `true` | Stage 1 mode: one `qwen --acp` child per daemon (bound to one workspace at boot, per [#3803](https://github.com/QwenLM/qwen-code/issues/3803) §02); N sessions multiplex onto that child via ACP `newSession()`. Stage 2 native in-process becomes available later. |
|
|
@@ -315,7 +321,7 @@ Notes:
|
|
|
315
321
|
| `--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). |
|
|
316
322
|
| `--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. |
|
|
317
323
|
|
|
318
|
-
> **Sizing the load knobs.** `--max-sessions` is the **new-child** cap.
|
|
324
|
+
> **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.
|
|
319
325
|
> Three other layers also limit load — when sizing for a high-concurrency
|
|
320
326
|
> deployment, tune them together:
|
|
321
327
|
>
|
|
@@ -327,11 +333,16 @@ Notes:
|
|
|
327
333
|
> - **per-session prompt admissions**:
|
|
328
334
|
> `--max-pending-prompts-per-session=5` bounds queued + active prompts
|
|
329
335
|
> accepted for one session. Overflow gets `503` with `Retry-After: 5`.
|
|
336
|
+
> - **daemon-wide fresh sessions**: `--max-total-sessions=N` bounds fresh
|
|
337
|
+
> session creation across the daemon. Overflow gets the same
|
|
338
|
+
> `session_limit_exceeded` shape with `scope: "total"`.
|
|
330
339
|
> - **per-subscriber backlog**: a 256-frame queue per SSE client; an
|
|
331
340
|
> over-capacity client gets a terminal `client_evicted` frame and is
|
|
332
341
|
> closed (one slow consumer can't pin the daemon).
|
|
333
342
|
>
|
|
334
|
-
> These caps interact: `--max-sessions
|
|
343
|
+
> These caps interact: the lower of `--max-sessions` and
|
|
344
|
+
> `--max-total-sessions` bounds fresh sessions in today's single-workspace
|
|
345
|
+
> daemon. `--max-sessions × 64 subscribers × 256 frames`
|
|
335
346
|
> is the worst-case in-flight memory at the EventBus layer, while
|
|
336
347
|
> `--max-sessions × --max-pending-prompts-per-session` bounds accepted
|
|
337
348
|
> prompt work at the admission layer. Default sizing assumes single-user /
|
|
@@ -359,6 +370,7 @@ Notes:
|
|
|
359
370
|
- **`LOOPBACK_BINDS` includes IPv6** — `::1` and `[::1]` count as loopback for the no-token rule.
|
|
360
371
|
- **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.
|
|
361
372
|
- **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.
|
|
373
|
+
- **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.
|
|
362
374
|
- **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.
|
|
363
375
|
- **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.
|
|
364
376
|
- **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.
|
|
@@ -413,21 +425,23 @@ To handle multiple **users** (each with their own quota, audit log, sandbox) or
|
|
|
413
425
|
|
|
414
426
|
## Loading and resuming a persisted session
|
|
415
427
|
|
|
416
|
-
The daemon exposes ACP's `session/load` and resume flow over HTTP
|
|
428
|
+
The daemon exposes ACP's `session/load` and resume flow over HTTP, plus a separate read-only transcript pager:
|
|
417
429
|
|
|
418
|
-
| Route
|
|
419
|
-
|
|
|
420
|
-
| `POST /session/:id/load`
|
|
421
|
-
| `POST /session/:id/resume`
|
|
430
|
+
| Route | Use when |
|
|
431
|
+
| ----------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
432
|
+
| `POST /session/:id/load` | The client has **no** useful local history rendered (cold reconnect, picker-then-open). For a live session, the daemon returns and injects the current bounded replay snapshot window; if older replay was dropped, the snapshot begins with `history_truncated`. Capability tag: `session_load`. |
|
|
433
|
+
| `POST /session/:id/resume` | The client already has the turns on screen and only needs the daemon-side handle back. Model context is restored on the agent side without UI replay — the SSE stream stays clean. Capability tag: `session_resume` (`unstable_session_resume` remains a deprecated alias for older clients). |
|
|
434
|
+
| `GET /session/:id/transcript` | The client needs the complete active persisted transcript. It returns id-less replay frames in cursor pages and does not call `/load`, attach a client, seed the live EventBus, create a live session, or change the live replay window. Capability tag: `session_transcript`. |
|
|
422
435
|
|
|
423
|
-
|
|
436
|
+
For load and resume, the TypeScript SDK exposes static factories on
|
|
437
|
+
`DaemonSessionClient`:
|
|
424
438
|
|
|
425
439
|
```ts
|
|
426
440
|
import { DaemonClient, DaemonSessionClient } from '@qwen-code/sdk';
|
|
427
441
|
|
|
428
442
|
const client = new DaemonClient({ baseUrl: 'http://127.0.0.1:4170' });
|
|
429
443
|
|
|
430
|
-
// Cold reconnect — daemon will replay
|
|
444
|
+
// Cold reconnect — daemon will replay the bounded snapshot window through SSE.
|
|
431
445
|
const session = await DaemonSessionClient.load(client, 'persisted-id');
|
|
432
446
|
|
|
433
447
|
// Or, if your UI already has the history, skip the replay:
|
|
@@ -439,9 +453,19 @@ for await (const event of session.events()) {
|
|
|
439
453
|
}
|
|
440
454
|
```
|
|
441
455
|
|
|
442
|
-
Pre-flight `caps.features.session_load`
|
|
456
|
+
Pre-flight `caps.features.session_load`, `caps.features.session_resume`, or `caps.features.session_transcript` before calling the matching route — older daemons return `404`. `unstable_session_resume` is still advertised as a deprecated compatibility alias. Concurrent same-action requests for the same id coalesce; cross-action races (a `load` racing a `resume`) get `409 restore_in_progress` with `Retry-After: 5`. See the [protocol reference](../developers/qwen-serve-protocol.md) for the full error envelope.
|
|
457
|
+
|
|
458
|
+
For full persisted replay, page with `DaemonClient.getSessionTranscriptPage(sessionId, { cursor, limit })` or the raw REST route:
|
|
459
|
+
|
|
460
|
+
```bash
|
|
461
|
+
curl "http://127.0.0.1:4170/session/$SESSION_ID/transcript?limit=100"
|
|
462
|
+
```
|
|
463
|
+
|
|
464
|
+
`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.
|
|
465
|
+
|
|
466
|
+
For repeated cold-session transcript paging, set `--channel-idle-timeout-ms` to a positive value. With the default `0`, an idle workspace's ACP child — and the in-process transcript index cache it holds — is reaped after every page, so each page re-spawns the child and rebuilds the index by re-scanning the whole frozen prefix (`O(snapshotSize)` per page). A positive timeout keeps the child alive across the cursor walk so it reuses its cached transcript index and replay config.
|
|
443
467
|
|
|
444
|
-
Note: history replay is bounded by the SSE ring
|
|
468
|
+
Note: live-session history replay is bounded twice: by the SSE ring for `Last-Event-ID` reconnects and by `--compacted-replay-max-bytes` for the snapshot returned by `POST /session/:id/load`. Long histories with chatty turns can exceed either bound. The daemon surfaces snapshot truncation with `history_truncated`; use `/transcript` when you need the complete active persisted history.
|
|
445
469
|
|
|
446
470
|
## Durability model
|
|
447
471
|
|
|
@@ -449,7 +473,7 @@ Note: history replay is bounded by the SSE ring (default 8000 frames). Long hist
|
|
|
449
473
|
|
|
450
474
|
- A child process crash publishes `session_died` and removes the live session from the daemon's maps. The persisted on-disk session **can** be reloaded via `POST /session/:id/load` if a fresh agent child is spawnable.
|
|
451
475
|
- A daemon restart loses every in-flight live session. The persisted sessions remain on disk and can be loaded against a new daemon process, subject to the same workspace binding rules.
|
|
452
|
-
- Long client disconnects (>5 min on a chatty turn) can outrun the SSE replay ring (default 8000 frames) — `Last-Event-ID` reconnect
|
|
476
|
+
- Long client disconnects (>5 min on a chatty turn) can outrun the SSE replay ring (default 8000 frames) — `Last-Event-ID` reconnect triggers `state_resync_required`. For mobile / flaky-network clients, plan to re-open SSE on long drops or call `POST /session/:id/load` to recover the current bounded replay snapshot; do not assume that route returns the full transcript.
|
|
453
477
|
- File operations (`writeTextFile`) are atomic across crashes (write-then-rename); they aren't atomic across daemon restarts in the sense of replaying — the file write either landed or it didn't.
|
|
454
478
|
|
|
455
479
|
If your integration needs server-side cross-restart durability beyond what `session/load` covers (e.g. server-managed retry queues), you still need application-level state recovery. Don't hold long-running, restart-sensitive state inside the daemon's session.
|
|
@@ -10,7 +10,7 @@ This document lists the available keyboard shortcuts in Qwen Code.
|
|
|
10
10
|
| `Ctrl+C` | Cancel the ongoing request and clear the input. Press twice to exit the application. |
|
|
11
11
|
| `Ctrl+D` | Exit the application if the input is empty. Press twice to confirm. |
|
|
12
12
|
| `Ctrl+L` | Clear the screen. |
|
|
13
|
-
| `Ctrl+O` |
|
|
13
|
+
| `Ctrl+O` | Open/close the full-detail transcript view (a scrollable, frozen snapshot showing every tool's complete output and full thinking). Press again, or `Esc`/`q`, to close. |
|
|
14
14
|
| `Ctrl+S` | Allows long responses to print fully, disabling truncation. Use your terminal's scrollback to view the entire output. |
|
|
15
15
|
| `Ctrl+T` | Toggle the display of tool descriptions. |
|
|
16
16
|
| `Ctrl+B` | While a foreground shell command is running: promote it to a background task. The child keeps running, the agent's turn unblocks, and the shell appears in `/tasks` + the Background tasks dialog. No-op when no shell is executing — Ctrl+B then falls through to its prompt-area binding (cursor-left). |
|
package/bundled/review/DESIGN.md
CHANGED
|
@@ -44,9 +44,12 @@ Empirically, ensemble diversity drops sharply past 3-5 sampled paths. Three is t
|
|
|
44
44
|
**Considered:**
|
|
45
45
|
|
|
46
46
|
- **N independent agents (original design):** One verification agent per finding. Each reads code independently. High quality but cost scales linearly with finding count (15 findings = 15 LLM calls).
|
|
47
|
-
- **1 batch agent (
|
|
47
|
+
- **1 batch agent (original):** Single agent receives all findings, verifies each one. Fixed cost.
|
|
48
|
+
- **Sharded batches, ≤8 findings each (chosen):** `ceil(N/8)` agents, launched together.
|
|
48
49
|
|
|
49
|
-
**Decision:**
|
|
50
|
+
**Decision:** Shard. One batch agent was right when a review produced 15 findings — it saw cross-finding relationships and cost O(1). But a Step 3B review of a large PR produces 30-60 findings, and one agent re-reading code for each of them inside a single context window degrades on the tail of the list. Sharding costs `ceil(N/8)` calls instead of 1, still far below one-agent-per-finding, and keeps each verifier's job small enough to do properly.
|
|
51
|
+
|
|
52
|
+
**A verifier may never reject a Critical.** It may downgrade to low confidence, with specific contradicting code cited. A rejected Critical is deleted from both the PR and the terminal and no later stage revisits it; a downgraded one still reaches a human under "Needs Human Review". The asymmetry between a false positive (noise) and a deleted true positive (a shipped bug plus another `/review` round) is not close.
|
|
50
53
|
|
|
51
54
|
## Why reverse audit is a separate step, and why iterative
|
|
52
55
|
|
|
@@ -59,13 +62,56 @@ Verification is targeted (check specific claims at specific locations). Reverse
|
|
|
59
62
|
|
|
60
63
|
### Why iterative (multi-round)
|
|
61
64
|
|
|
62
|
-
A single reverse audit pass leaves whatever the reverse audit agent itself missed. Each new round receives the cumulative finding list from prior rounds, so it focuses on what's left undiscovered.
|
|
65
|
+
A single reverse audit pass leaves whatever the reverse audit agent itself missed. Each new round receives the cumulative finding list from prior rounds, so it focuses on what's left undiscovered.
|
|
66
|
+
|
|
67
|
+
### Why the stop rule is two consecutive dry rounds, not one
|
|
68
|
+
|
|
69
|
+
One dry round was the original rule, and PR #6457 shows why it is unsound. The per-round Critical yield across its eight review rounds was `2, 2, 7, 0, 0, 5, 3, 1`. The review returned "no blockers" **twice**, and the next round surfaced five Criticals — three of them in code that had been in the diff since the first commit. A yield of zero is evidence about one round's agents, not about the code.
|
|
70
|
+
|
|
71
|
+
Requiring two consecutive dry rounds makes a single lazy or context-starved agent unable to end the loop. The hard cap moves from 3 rounds to 5, and when the cap is what stopped the loop the output must say so rather than implying convergence.
|
|
72
|
+
|
|
73
|
+
### Why the reverse audit fans out per chunk
|
|
74
|
+
|
|
75
|
+
The original design gave one agent the whole diff plus a growing cumulative finding list. On a 5 800-line diff that is the most context-starved agent in the pipeline — exactly on the PRs where reverse audit matters most. Under Step 3B each round runs one auditor per chunk, each with the full cumulative finding list but only its own territory to re-read.
|
|
76
|
+
|
|
77
|
+
### Why the topology gate counts source lines, not diff lines
|
|
78
|
+
|
|
79
|
+
Diff size is a bad proxy for review risk, because tests dominate it. Across this repo's last 40 merged PRs the median diff is **41% test code**, and 14 of the 40 are more than half tests. A gate on raw diff lines sends a change of 173 production lines that ships 489 lines of new tests into the territory fan-out, where the production code ends up owned by a single chunk agent — while under the dimension fan-out it would have been read by eight lenses.
|
|
80
|
+
|
|
81
|
+
Territory fan-out is worth it when there is a lot of _risky_ code to divide, not a lot of _lines_. So the gate is `srcDiffLines > 500`, with a second clause `diffLines > 2400` as a delivery bound: past that point `ceil(diffLines / 400) + 4 > 10`, so chunking uses fewer agents than the ten-lens topology anyway, and asking ten agents each to read a diff that large dilutes all of them. On the 40-PR sample the second clause never fires; it exists for a changeset dominated by tests or generated files.
|
|
82
|
+
|
|
83
|
+
Re-gating moves 6 of those 40 PRs from 3B back to 3A and costs 22 extra agents in total across all 40 — about 5%. It buys those six PRs eight review lenses on their production code instead of one.
|
|
84
|
+
|
|
85
|
+
Chunking itself is unchanged: the plan still tiles every line, tests and generated files included. Only the count of reviewers and their brief change. `heavy` is likewise restricted to `source` files — the invariant checklist asks about fields, timers, collections, and error taxonomies, and a rewritten test file has none of those.
|
|
86
|
+
|
|
87
|
+
### Why `plan-diff` exists
|
|
88
|
+
|
|
89
|
+
Step 3B's chunk agents are defined as "one per entry in `chunks[]`", and only `fetch-pr` produced a chunk plan. A local-diff review, or a cross-repo review in lightweight mode, therefore routed into a topology it had no chunk list for: no receipts, no tiling guarantee, and the orchestrator left to improvise line ranges. Two of the four review paths were promised a mechanism the skill could not deliver.
|
|
90
|
+
|
|
91
|
+
`qwen review plan-diff <diff-file>` reads a captured diff and emits the same `chunks[]`, `files[]` and topology counts. Redirecting `git diff` or `gh pr diff` to a file already bypasses the 30 000-char shell cap, so all four paths now share one code path. It cannot decide `heavy` — that needs a tree to read the post-change file from — so a bare diff gets chunk agents but no invariant agents.
|
|
92
|
+
|
|
93
|
+
### Why the topology gate ignores prose
|
|
63
94
|
|
|
64
|
-
|
|
95
|
+
`docs/**` and root-level markdown classify as `docs` and stay out of `srcDiffLines`. A translation PR carries no runtime risk, and gating on raw size would fan chunk agents across it. Markdown _inside a source tree_ stays `source`: this repo's bundled skill prompts are `packages/core/src/skills/**/SKILL.md`, and they are executable behaviour. Coverage is unaffected either way — every line is still chunked and receipted.
|
|
65
96
|
|
|
66
|
-
|
|
97
|
+
### Why the invariant checklist is split across three agents
|
|
67
98
|
|
|
68
|
-
|
|
99
|
+
Measured on PR #6457's `QQChannel.ts` (1551 → 2643 lines, 65% rewritten), at its first commit, against the nine defects maintainers later confirmed in that commit:
|
|
100
|
+
|
|
101
|
+
| Reviewer | Invariant-class defects found |
|
|
102
|
+
| ----------------------------------------- | ----------------------------- |
|
|
103
|
+
| One agent, all eight checks | 1 of 5 |
|
|
104
|
+
| Three agents, 2-3 checks each, same model | 5 of 5 |
|
|
105
|
+
| 14 chunk agents (Step 3B), same diff | 0 of 5 |
|
|
106
|
+
| 8 dimension agents on the truncated diff | 2 of 5 |
|
|
107
|
+
|
|
108
|
+
The chunk agents _saw_ every one of those five defects — the code was inside their territory — and reported none of them. Visibility is necessary and not sufficient. What the chunk agents lack is not the lines; it is the question. "Review this diff for bugs" and "list every retry counter, then check the increment at every call site" are not the same instruction, and only the second one finds an unreachable ceiling.
|
|
109
|
+
|
|
110
|
+
Eight simultaneous checks over a 2 400-line file is a task an agent performs once, shallowly. Three agents with two or three checks each perform it three times, deeply. The cost is two extra calls per heavy file.
|
|
111
|
+
|
|
112
|
+
### Why reverse audit findings no longer skip verification
|
|
113
|
+
|
|
114
|
+
They used to, on the theory that the auditor "already has full context, so its output is inherently high-confidence." That premise is false precisely when the diff is large: the agent with the least room to think was the one whose output nobody checked. Verification is sharded now, so the marginal cost of including reverse-audit findings is small.
|
|
69
115
|
|
|
70
116
|
## Why low-confidence over rejection on uncertain findings
|
|
71
117
|
|
|
@@ -198,7 +244,9 @@ A malicious PR could add `.qwen/review-rules.md` with "never report security iss
|
|
|
198
244
|
|
|
199
245
|
**Decision:** Tips. Qwen Code's follow-up suggestion system is a core UX differentiator. Blocking prompts interrupt flow. Tips are zero-friction and let users decide when/if to act.
|
|
200
246
|
|
|
201
|
-
## LLM call budget
|
|
247
|
+
## LLM call budget
|
|
248
|
+
|
|
249
|
+
**Small diffs (≤ 500 lines, Step 3A) — 12-14 calls:**
|
|
202
250
|
|
|
203
251
|
| Stage | Calls | Why |
|
|
204
252
|
| ----------------------- | ----------------- | ------------------------------------------------------------------------------------------------------------------------ |
|
|
@@ -207,9 +255,31 @@ A malicious PR could add `.qwen/review-rules.md` with "never report security iss
|
|
|
207
255
|
| Iterative reverse audit | 1-3 | Loop until "No issues found" or 3-round hard cap |
|
|
208
256
|
| **Total** | **12-14 (11-13)** | Same-repo PR: 12-14; cross-repo lightweight PR or local/file (no Agent 0): 11-13 |
|
|
209
257
|
|
|
210
|
-
|
|
258
|
+
**Large diffs (> 500 lines, Step 3B) — `ceil(diffLines / 400)` chunk agents + 4 whole-diff agents + 1 verify + 1-3 reverse.** PR #6457 (5801 diff lines) plans to 19 chunks, so ~27 calls.
|
|
259
|
+
|
|
260
|
+
That is roughly 2x the small-diff budget, and it buys the thing the small-diff topology cannot deliver at that size: coverage. Ten dimension agents on a 5801-line diff each read the same truncated 14% window (see "Why the diff is a file, not a command"), so nine of the ten calls are redundant reads of the same hunks. Nineteen chunk agents each read a distinct ~390-line territory, and every line of the diff has exactly one accountable owner. The comparison to make is not 27 calls vs 14: PR #6457 took **eight** review rounds at 12-14 calls each — over 100 calls — and was still surfacing Criticals in code that had been in the diff since the first commit.
|
|
261
|
+
|
|
262
|
+
Competitors: Copilot uses 1 call, Gemini uses 2, Claude /ultrareview uses 5-20 (cloud). Ours biases toward higher recall — the assumption is that "find more issues per round" is more valuable than minimizing per-run cost, because every missed issue forces the user into another `/review` iteration.
|
|
263
|
+
|
|
264
|
+
## Why the diff is a file, not a command
|
|
265
|
+
|
|
266
|
+
Agents used to be handed `git diff main...HEAD` and told to run it. Shell tool output passes through `truncateToolOutput` with `ShellTool.maxOutputChars = 30_000` and `keep: 'both'`, which allocates `threshold / 5` characters to the head and the remainder to the tail.
|
|
267
|
+
|
|
268
|
+
On PR #6457's 211 000-character diff that yields a 6 000-char head (`QQChannel.ts` lines 41-250) and a 24 000-char tail (`stream.test.ts` and `types.ts`, which sort last by path and together changed 9 lines). 85.8% of the diff — including 19 of the 20 Criticals eventually reported on that PR — was replaced by a `[CONTENT TRUNCATED]` marker. Every agent saw the same window, so the ten-way dimension fan-out multiplied redundancy rather than coverage, and each round of `/review` sampled a different subset of the bugs depending on which files an agent happened to `read_file` on its own initiative.
|
|
269
|
+
|
|
270
|
+
`fetch-pr` now writes the diff to `.qwen/tmp/qwen-review-pr-<n>-diff.txt` and emits a chunk plan. `read_file` overrides `maxOutputChars` to `Infinity`, so it escapes the scheduler's head/tail mangling — but `processSingleFileContent` still caps one read at `truncateToolOutputThreshold` (25 000 chars), sets `isTruncated`, and expects the caller to page. Writing the diff to a file is therefore necessary but **not sufficient**: a single `read_file` over PR #6457's diff returns lines 1-611 and stops.
|
|
271
|
+
|
|
272
|
+
The chunk plan is what closes the gap. Chunks are bounded by **both** a line budget (attention) and a character budget (`MAX_CHUNK_CHARS`, 20 000 — under the 25 000 read cap, so a chunk never comes back short), and they tile the diff exactly (`chunksCoverDiff` asserts no gap, no overlap). Exact tiling is what makes the Step 3B coverage receipts checkable: a chunk with no receipt is a territory nobody reviewed.
|
|
273
|
+
|
|
274
|
+
Measured on PR #6457's real 211 000-char diff, driving the production `truncateAndSaveToFile` and `processSingleFileContent`:
|
|
275
|
+
|
|
276
|
+
| What the agent is given | Chars delivered | Diff covered | Of the 20 Criticals eventually found, in view |
|
|
277
|
+
| ------------------------------------------ | --------------- | ------------ | --------------------------------------------- |
|
|
278
|
+
| `git diff` via shell (the old way) | 30 468 | 14.4% | 1 |
|
|
279
|
+
| Diff in a file, read whole (no chunk plan) | 25 015 | 10.5% | — |
|
|
280
|
+
| Diff in a file + 19-chunk plan | 210 900 | **100%** | **20** |
|
|
211
281
|
|
|
212
|
-
|
|
282
|
+
Chunk boundaries fall on hunk boundaries wherever they can, because a boundary inside a hunk risks cutting a function in half. A hunk larger than the target is the exception: it is split, but only at a column-0 source line preceded by a blank line — a top-level declaration. A brand-new file arrives as one enormous hunk (`events.test.ts` was a single 1535-line hunk), so treating hunks as strictly atomic would hand one agent a 50 000-char territory and defeat the whole point. When no such boundary exists the hunk stays whole and the chunk is flagged `oversized`.
|
|
213
283
|
|
|
214
284
|
## Why cross-repo uses lightweight mode
|
|
215
285
|
|
|
@@ -228,28 +298,29 @@ Key implementation detail: Step 7 must use the owner/repo extracted from the URL
|
|
|
228
298
|
|
|
229
299
|
**Decision:** Auto-discovery. Every project already defines its tool chain in CI config. Reading those files leverages existing knowledge without asking users to duplicate it. The LLM is capable of parsing YAML workflow files and extracting the relevant commands. Falls back gracefully: if no CI config exists, the build/test discovery is simply skipped and LLM agents still review the diff.
|
|
230
300
|
|
|
231
|
-
## Why Suggestion-level findings
|
|
301
|
+
## Why Suggestion-level findings are posted as inline comments, like Critical
|
|
232
302
|
|
|
233
303
|
**Considered:**
|
|
234
304
|
|
|
235
|
-
- **
|
|
236
|
-
- **Critical inline, Suggestion in
|
|
237
|
-
- **
|
|
305
|
+
- **Critical inline, Suggestion in the review `body`:** splits by severity, but the review body is a frozen artifact of one review submission — every new /review run appends a new review with its own body, so Suggestion lists accumulate across runs and never converge.
|
|
306
|
+
- **Critical inline, Suggestion in one updatable issue comment:** Suggestion findings go to a single PR issue comment located by author + embedded marker and PATCHed in place on every run, so the list refreshes rather than grows. Shipped for a while; reverted for the reasons below.
|
|
307
|
+
- **Both severities inline, distinguished by a `**[Critical]**`/`**[Suggestion]**` body prefix (chosen):** every high-confidence finding is pinned to its code line and carries a one-click ` ```suggestion ` block. Severity is communicated in the comment text, not by the channel it arrives on.
|
|
238
308
|
|
|
239
|
-
**Decision:**
|
|
309
|
+
**Decision:** Both inline. The updatable-summary design optimized for a convergence problem, but it paid for that with two costs that turned out to dominate:
|
|
240
310
|
|
|
241
|
-
|
|
311
|
+
1. **A summary comment can never collapse.** GitHub marks an inline review thread **Outdated** and folds it away as soon as the author edits the line it is anchored to. So an addressed inline finding removes itself from the page. An issue comment has no such lifecycle — it sits in the PR conversation permanently, one extra comment whether or not its rows still apply. PATCHing it to "all suggestions addressed" replaces the content but not the comment. The very mechanism intended to prevent clutter _was_ the clutter.
|
|
312
|
+
2. **A Markdown table cannot carry a one-click fix.** GitHub renders a ` ```suggestion ` fence as an applicable change only inside a review comment on a diff line; in an issue comment it degrades to a plain code block. Suggestion-level findings — mechanical, localized cleanups — are precisely the class that benefits most from one-click apply, so the split withheld the feature from the findings that most needed it. The table's cramped "Suggested fix" column also degraded badly as the suggestion count grew.
|
|
242
313
|
|
|
243
|
-
|
|
314
|
+
The convergence concern that motivated the summary is real but narrower than it looked: GitHub's Outdated-collapse handles every suggestion the author actually acts on, which is the common case. What remains is a suggestion the author declines and leaves untouched — its line does not change, so the thread stays open and a later run can post a near-duplicate. That residue is bounded by the presubmit Overlap check (`blockOnExistingComments`), which blocks submission when a new finding lands on the same `(path, line)` as a live Qwen comment on the same commit.
|
|
244
315
|
|
|
245
316
|
**Trade-off:**
|
|
246
317
|
|
|
247
|
-
- ✅
|
|
248
|
-
- ✅
|
|
249
|
-
- ✅
|
|
250
|
-
- ❌
|
|
251
|
-
-
|
|
252
|
-
- ❌ Pattern-aggregated Suggestion findings (the multi-occurrence `Pattern:` form)
|
|
318
|
+
- ✅ Suggestion findings regain one-click ` ```suggestion ` apply and sit next to the code in "Files changed."
|
|
319
|
+
- ✅ Addressed findings self-collapse via GitHub's Outdated mechanism; no permanent extra comment on the PR page.
|
|
320
|
+
- ✅ One posting path for both severities — the `comments` array — instead of a review submission plus a second issue-comment API call.
|
|
321
|
+
- ❌ Suggestions now share the atomic `POST /pulls/{n}/reviews` call with Criticals. That call is all-or-nothing: one entry anchored to a line outside the diff 422s the whole review, so a mis-anchored Suggestion can suppress a Critical blocker. Previously Suggestions travelled on a separate, line-agnostic issue-comment call where a bad anchor was impossible. Step 7 mitigates with a 422 fallback rather than pre-validating every anchor up front: GitHub's 422 does not identify the offending entry, so the fallback has the model recheck each anchor against the diff, relocate failing Criticals into `body` (failing Suggestions are discarded — Suggestion text must stay off the `body` channel, which `qwen-autofix.yml` does not filter), and resubmit — degrading to an all-prose review of the blockers rather than posting nothing.
|
|
322
|
+
- ❌ A declined suggestion on an unchanged line can be re-posted by a later run on a new commit: the presubmit Overlap check only compares against comments whose `commit_id` matches the commit under review, so prior comments are bucketed `stale` after any push. Closing this fully needs a resolve/minimize step (GraphQL `resolveReviewThread` / `minimizeComment`) that folds our own superseded threads before submitting a new review.
|
|
323
|
+
- ❌ Pattern-aggregated Suggestion findings (the multi-occurrence `Pattern:` form) must pick a representative line to anchor to; the full structured aggregation remains visible in the terminal output.
|
|
253
324
|
|
|
254
325
|
## Rejected alternatives
|
|
255
326
|
|