@qwen-code/qwen-code 0.19.9 → 0.19.10-nightly.20260715.c538bd70d
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/settings.md +8 -0
- package/bundled/qc-helper/docs/features/channels/dingtalk.md +3 -0
- package/bundled/qc-helper/docs/features/channels/overview.md +11 -3
- package/bundled/qc-helper/docs/features/code-review.md +88 -44
- package/bundled/qc-helper/docs/features/commands.md +6 -6
- package/bundled/qc-helper/docs/features/hooks.md +29 -0
- package/bundled/qc-helper/docs/features/sub-agents.md +21 -0
- package/bundled/qc-helper/docs/qwen-serve.md +46 -20
- package/bundled/qc-helper/docs/reference/keyboard-shortcuts.md +1 -1
- package/bundled/review/DESIGN.md +219 -33
- package/bundled/review/SKILL.md +449 -153
- package/chunks/{MaxSizedBox-6HYJ3ZPJ.js → MaxSizedBox-LOOBDOTZ.js} +31 -31
- package/chunks/{StandaloneSessionPicker-WS4GXYOG.js → StandaloneSessionPicker-76CLZSH2.js} +51 -49
- package/chunks/{acpAgent-5FWSOZL5.js → acpAgent-DFJOK7EB.js} +567 -482
- package/chunks/{agent-HOLHB3RT.js → agent-OJQWXNHP.js} +26 -26
- package/chunks/{agent-headless-SXL6H56A.js → agent-headless-ND7GOORY.js} +26 -26
- package/chunks/{anthropicContentGenerator-L73TO3DB.js → anthropicContentGenerator-56U3YCP2.js} +5 -5
- package/chunks/{artifact-tool-U7IRTLIV.js → artifact-tool-MIZLXBLF.js} +2 -2
- package/chunks/{askUserQuestion-2DLUUZZG.js → askUserQuestion-XMJVPNWM.js} +2 -2
- package/chunks/{bridge-BCJQ7BBB.js → bridge-SNJK2JLK.js} +32 -32
- package/chunks/{ca-UJ6HC4H2.js → ca-C2JTT2N5.js} +4 -1
- package/chunks/channel-worker-group-K7QKH6IG.js +13 -0
- package/chunks/channel-worker-manager-YSFB4HUU.js +418 -0
- package/chunks/channel-worker-supervisor-QCKIOYZD.js +20 -0
- package/chunks/{chunk-CCDMCZPP.js → chunk-26UABK2C.js} +1 -1
- package/chunks/{chunk-LXNEJD34.js → chunk-2FO5GFKZ.js} +1 -1
- package/chunks/{chunk-L36P3NBD.js → chunk-2W3OOD4W.js} +2 -0
- package/chunks/{chunk-7R3CZA3Q.js → chunk-2Z5UX4KI.js} +1 -1
- package/chunks/chunk-3MX7D6QN.js +516 -0
- package/chunks/{chunk-CLFKOZEK.js → chunk-3XULHBC7.js} +1 -1
- package/chunks/chunk-4QZADKJS.js +34 -0
- package/chunks/{chunk-E2IY465S.js → chunk-4UDHAJFV.js} +10 -10
- package/chunks/{chunk-5U27BZ72.js → chunk-55SAYASQ.js} +190 -77
- package/chunks/chunk-5HBA2Z7V.js +28 -0
- package/chunks/{chunk-ZJE7NSL3.js → chunk-5M6IDOMF.js} +34 -97
- package/chunks/{chunk-HVY356M2.js → chunk-5SQ55XJZ.js} +3 -3
- package/chunks/{chunk-EAXLGSFP.js → chunk-6GRAQNTY.js} +8 -8
- package/chunks/{chunk-K434OPES.js → chunk-6WPJRYLZ.js} +3 -3
- package/chunks/{chunk-VWPVHAMO.js → chunk-7DXHIMXL.js} +5 -1
- package/chunks/{chunk-J4TSQ5EQ.js → chunk-7NFB7LBQ.js} +8 -1
- package/chunks/{chunk-5SFOMFG4.js → chunk-7R4DPSRA.js} +36 -2
- package/chunks/{chunk-W2UIXQDL.js → chunk-7STBROEV.js} +1 -1
- package/chunks/{chunk-BMGVRZN4.js → chunk-AETEZU4U.js} +1 -1
- package/chunks/{chunk-HPDUL5MF.js → chunk-AGZWN4WV.js} +1 -1
- package/chunks/{chunk-RDJSGHZE.js → chunk-ALGGS7UH.js} +1 -38
- package/chunks/{chunk-GWHYEOV2.js → chunk-ARKANCNX.js} +2 -1
- package/chunks/{chunk-34Z3U5RQ.js → chunk-AU4R7ACM.js} +518 -114
- package/chunks/{channel-worker-supervisor-WSOAAYUO.js → chunk-B7MFJBKE.js} +67 -35
- package/chunks/{chunk-524RQP4Q.js → chunk-B7NUUEXC.js} +10 -4
- package/chunks/{chunk-MC23XKFE.js → chunk-BLPMRN4B.js} +3 -3
- package/chunks/{chunk-A3XKDTSH.js → chunk-BRRKAUGR.js} +4 -4
- package/chunks/{chunk-W3JHSWRP.js → chunk-BUPLOPUA.js} +4 -4
- package/chunks/{chunk-3VAGG5T5.js → chunk-BYOOK22F.js} +121 -16
- package/chunks/{chunk-EMDMPBJ4.js → chunk-C47ENMZJ.js} +3 -3
- package/chunks/{chunk-3AHXV67U.js → chunk-C5TVXLJZ.js} +1 -1
- package/chunks/{chunk-X6TJ3YZV.js → chunk-CGT5TQIZ.js} +159 -57
- package/chunks/{chunk-4C5BQ2D2.js → chunk-CQQD7MDG.js} +82 -12
- package/chunks/{chunk-KKNMOEZH.js → chunk-E2UVIFI2.js} +185 -398
- package/chunks/{chunk-P67PFTUT.js → chunk-EPY4ZEU6.js} +4 -4
- package/chunks/{chunk-6PESIEKJ.js → chunk-EZSRHWGS.js} +68 -6
- package/chunks/{chunk-4GORAUCN.js → chunk-F333XEAU.js} +1 -1
- package/chunks/{chunk-YLV3XXFB.js → chunk-FBU7WRZI.js} +17 -1
- package/chunks/{chunk-Q6I5QHMH.js → chunk-FGTCZJOD.js} +1 -1
- package/chunks/{chunk-LK7KUE55.js → chunk-FSA7ERJ2.js} +1 -1
- package/chunks/{chunk-B4G2WOFA.js → chunk-FTU55ZZ7.js} +3 -3
- package/chunks/{chunk-EGYZ275M.js → chunk-GCD7PEDZ.js} +4 -4
- package/chunks/{chunk-SJHDYNHF.js → chunk-GDQQRV43.js} +3 -3
- package/chunks/{chunk-3BTRNWWD.js → chunk-GOXKNSDZ.js} +1 -64
- package/chunks/{chunk-GZRTYZ2R.js → chunk-GQKWT45X.js} +154 -1
- package/chunks/chunk-GYTKQYDB.js +23 -0
- package/chunks/{chunk-YVEDCAUE.js → chunk-HGT6JR3U.js} +19 -0
- package/chunks/{chunk-N3EMS75V.js → chunk-HKM2TEF7.js} +1268 -1233
- package/chunks/chunk-IDS7MSUP.js +74 -0
- package/chunks/{chunk-PVZM22CK.js → chunk-INKAZNYC.js} +5 -5
- package/chunks/{chunk-3G7CIHVV.js → chunk-IOQ75R4R.js} +4 -4
- package/chunks/{chunk-AURZZYMD.js → chunk-IQOOJSPR.js} +4 -4
- package/chunks/{chunk-AKIVHSJR.js → chunk-J4UQXGVL.js} +111 -93
- package/chunks/{chunk-VTU57BWZ.js → chunk-JEKOCLRN.js} +1 -1
- package/chunks/{chunk-B5ORX3FG.js → chunk-JGUT3LWZ.js} +6 -1
- package/chunks/{chunk-PDZ7MBDG.js → chunk-JIQHT2O6.js} +27 -6
- package/chunks/{chunk-XUOOWUZS.js → chunk-JPAH76WA.js} +1 -1
- package/chunks/{chunk-BAPGSC7U.js → chunk-K4LG6TVJ.js} +23 -9
- package/chunks/chunk-KET3M4K4.js +493 -0
- package/chunks/{chunk-ORBGV7NJ.js → chunk-KKRTYONM.js} +6 -4
- package/chunks/{chunk-SCGXUJBT.js → chunk-KPA5QGN2.js} +3 -3
- package/chunks/{chunk-IAVB3HM3.js → chunk-KSO42X3Z.js} +13 -0
- package/chunks/{chunk-4QLQE5WJ.js → chunk-L5U3ELZX.js} +1 -1
- package/chunks/{chunk-77XJ3SWN.js → chunk-LIUEXYPL.js} +5 -5
- package/chunks/{chunk-4ZVWQHEQ.js → chunk-LQIDP6K5.js} +1 -1
- package/chunks/{chunk-34MW4KTI.js → chunk-LT5CRUA5.js} +2 -2
- package/chunks/{chunk-DC5Z3DU6.js → chunk-LXLTBIDP.js} +43 -6
- package/chunks/{chunk-VA4KQY6V.js → chunk-MEK7CQJR.js} +3 -3
- package/chunks/{chunk-KHUQZZJ6.js → chunk-MO7O5722.js} +370 -36
- package/chunks/chunk-MR3PXB6E.js +48 -0
- package/chunks/{chunk-PODM4SZ4.js → chunk-MV52C5MA.js} +5 -5
- package/chunks/{chunk-WL4L7EZH.js → chunk-OA4JVLSA.js} +27 -1
- package/chunks/{chunk-X4NKFGFJ.js → chunk-OJAMDVF5.js} +0 -15
- package/chunks/{chunk-BZIFY5TF.js → chunk-OKAIGAYW.js} +3 -3
- package/chunks/{chunk-ECNYL4AA.js → chunk-OPNPGO6D.js} +1 -1
- package/chunks/{chunk-3SJSQXNS.js → chunk-PEJWA3QQ.js} +12 -8
- package/chunks/{chunk-MXL5UP4X.js → chunk-Q6TUALBE.js} +1 -1
- package/chunks/{chunk-QYMQSECS.js → chunk-QAW7PIHT.js} +2 -2
- package/chunks/{chunk-5HOMWMCL.js → chunk-QEDYXCLG.js} +4167 -1338
- package/chunks/chunk-QNNTA7IX.js +91 -0
- package/chunks/{chunk-D2GFWEXB.js → chunk-QNT5T4Q2.js} +6 -6
- package/chunks/{chunk-CUMMSHH6.js → chunk-R5XLNDE2.js} +1 -1
- package/chunks/{chunk-LUODI7WD.js → chunk-RCEEXIOO.js} +6 -4
- package/chunks/{chunk-WQUQVGTF.js → chunk-RQWDLG6Z.js} +183 -96
- package/chunks/{chunk-XYQSMABS.js → chunk-SLCVZX6F.js} +4 -4
- package/chunks/{chunk-CEA3E3JB.js → chunk-SPJQO2CE.js} +2 -2
- package/chunks/{chunk-JSG7ZSBC.js → chunk-TQBODCFD.js} +1 -1
- package/chunks/chunk-TV6K2FB4.js +224 -0
- package/chunks/{chunk-5XUCZNSY.js → chunk-TYLOFD2U.js} +1 -1
- package/chunks/{chunk-CNEIYWSA.js → chunk-UKWAQ7AV.js} +5 -5
- package/chunks/{chunk-7PU4FYLG.js → chunk-US4YQT62.js} +4 -4
- package/chunks/{chunk-Q4YQW46J.js → chunk-UYPPCIJQ.js} +36 -18
- package/chunks/{chunk-YAUFFXDS.js → chunk-UZKG7HFG.js} +12 -11
- package/chunks/{chunk-GWLSNBG4.js → chunk-VQLRHJKL.js} +30 -26
- package/chunks/{chunk-L6P4KPHR.js → chunk-VRUHAVZS.js} +59 -39
- package/chunks/{chunk-HKMAENAO.js → chunk-W2U4RJJU.js} +8 -6
- package/chunks/{chunk-ATAKVRIF.js → chunk-WDSCTLAB.js} +7 -7
- package/chunks/{chunk-ZLXQN2QS.js → chunk-WIEO4CWB.js} +1 -1
- package/chunks/{chunk-OQ4DRIOA.js → chunk-WJOC24IW.js} +2 -2
- package/chunks/{chunk-J3JA76CF.js → chunk-WTNZ6DXB.js} +1 -1
- package/chunks/{chunk-NVYTCB5Z.js → chunk-XCR44EEA.js} +14589 -13980
- package/chunks/{chunk-TDEXZKMT.js → chunk-Y66VOBRP.js} +2 -2
- package/chunks/{chunk-CC2ITGCF.js → chunk-YHN5SUIJ.js} +49 -6
- package/chunks/{chunk-D5SKC4YG.js → chunk-ZBAROUR6.js} +41 -13
- package/chunks/{chunk-U2NNEY4R.js → chunk-ZEEVKDO7.js} +1 -1
- package/chunks/{chunk-5VXNZWGF.js → chunk-ZLOHB76C.js} +1 -1
- package/chunks/{chunk-JZETEN3Z.js → chunk-ZNCLTRNU.js} +3 -3
- package/chunks/{chunk-TO6QE22P.js → chunk-ZP4ZZJJG.js} +26 -22
- package/chunks/{chunk-UOZWKTQG.js → chunk-ZPAHLFZU.js} +2 -2
- package/chunks/{chunk-XLA62MYB.js → chunk-ZTXZ7FM4.js} +2 -2
- package/chunks/{computer-use-HM37RX6A.js → computer-use-DRETHEQF.js} +26 -26
- package/chunks/{config-utils-NV2SVGPB.js → config-utils-XSHOMMK4.js} +3 -2
- package/chunks/{contextCommand-OF5SFGH2.js → contextCommand-7YJUW23J.js} +28 -28
- package/chunks/{create-sub-session-INX7TXJI.js → create-sub-session-EAB2U5XW.js} +2 -2
- package/chunks/{create-sub-session-KPRT7ECJ.js → create-sub-session-L5HOZAEL.js} +26 -26
- package/chunks/{cron-create-AMBKGZX6.js → cron-create-GMVKSXZT.js} +4 -4
- package/chunks/{cron-delete-LRU4OJYG.js → cron-delete-VDZKUAVK.js} +4 -4
- package/chunks/{cron-list-WVP2JXER.js → cron-list-UG7C7RAR.js} +4 -4
- package/chunks/{daemon-ARHTSTXP.js → daemon-MI5HF26N.js} +603 -62
- package/chunks/{daemon-status-provider-B2UTZUJ2.js → daemon-status-provider-2D47GOJT.js} +36 -36
- package/chunks/{de-EBJN3OVN.js → de-2VCDRDGU.js} +4 -1
- package/chunks/{dist-WKPOYU7O.js → dist-IEXZC5ZR.js} +1 -1
- package/chunks/{dist-XKU3ABM5.js → dist-PQK4GCKA.js} +1 -1
- package/chunks/{dist-VHV4EVHG.js → dist-U75JK3PF.js} +1 -1
- package/chunks/{dist-R7LN5AZE.js → dist-VXO7QBON.js} +2 -2
- package/chunks/{dist-PNVLTKTL.js → dist-WH4TZSH3.js} +158 -4
- package/chunks/{dist-QYCAEZIT.js → dist-X2ABIW5R.js} +5 -1
- package/chunks/{earlyInputCapture-3VTHRC4C.js → earlyInputCapture-OJQPGCSX.js} +27 -27
- package/chunks/{edit-VNI4NJ73.js → edit-LZOAFLT2.js} +27 -27
- package/chunks/{en-5A6LA7RK.js → en-NJCA3TAA.js} +5 -1
- package/chunks/{enter-worktree-IYYBKPZT.js → enter-worktree-VL2TCGI5.js} +26 -26
- package/chunks/{enterPlanMode-GMC5FD5A.js → enterPlanMode-QGHBHZWI.js} +42 -27
- package/chunks/{environment-5HSSRMJ7.js → environment-2G5IC2EU.js} +30 -29
- package/chunks/{errors-HMFCJJ7P.js → errors-7QNQDNI2.js} +28 -28
- package/chunks/{exit-worktree-6BHCMP5G.js → exit-worktree-5V3X2MRH.js} +26 -26
- package/chunks/{exitPlanMode-CJW7G74W.js → exitPlanMode-XDPNQB4H.js} +26 -26
- package/chunks/{fast-path-FRDMDVHG.js → fast-path-NNSD4BVZ.js} +3 -3
- package/chunks/{fast-path-settings-5QZE64HR.js → fast-path-settings-DAOVFADC.js} +6 -4
- package/chunks/{fr-YXRABLYZ.js → fr-5F3E7WKD.js} +4 -1
- package/chunks/{gemini-QYSEGHAX.js → gemini-RKDUWGXJ.js} +139 -76
- package/chunks/{geminiContentGenerator-J7YNZDYP.js → geminiContentGenerator-V7QGHXVF.js} +4 -4
- package/chunks/{glob-BRCSGFOT.js → glob-ZNQUUOTS.js} +28 -27
- package/chunks/{grep-EE2DUOSY.js → grep-NYIGLM7R.js} +26 -26
- package/chunks/{handleAutoUpdate-5NWTLR67.js → handleAutoUpdate-UBQ4IQXY.js} +30 -30
- package/chunks/{i18n-BASPENNQ.js → i18n-5T3S6SGP.js} +27 -27
- package/chunks/initializer-TJED246Q.js +73 -0
- package/chunks/{installationInfo-W6V4DT7W.js → installationInfo-OTEU7FKR.js} +27 -27
- package/chunks/{ja-7VQOANVG.js → ja-66J6B53L.js} +5 -2
- package/chunks/{keychain-token-storage-VKUBNCP4.js → keychain-token-storage-AU22CQI6.js} +2 -2
- package/chunks/list-6OHS7MFJ.js +76 -0
- package/chunks/loadedSettingsAdapter-5456XIYG.js +70 -0
- package/chunks/{loop-wakeup-WX7BLWWP.js → loop-wakeup-C4EXLYVA.js} +5 -5
- package/chunks/{ls-ZB2BDP6T.js → ls-2OUTG3IZ.js} +4 -4
- package/chunks/{lsp-V5DN4YUJ.js → lsp-4VMNXZW6.js} +2 -2
- package/chunks/mcp-NOWRPFKY.js +70 -0
- package/chunks/{monitor-X5Z3H4FY.js → monitor-CVDZO3IZ.js} +26 -26
- package/chunks/nonInteractiveCli-UGI7Y43E.js +129 -0
- package/chunks/{notebook-edit-TRM2QIJK.js → notebook-edit-NWIA5TEJ.js} +27 -27
- package/chunks/{openaiContentGenerator-AAYONPMF.js → openaiContentGenerator-KJONBBJO.js} +12 -12
- package/chunks/{pidfile-D4I4FTVA.js → pidfile-L3TGXMTF.js} +27 -27
- package/chunks/{pt-2C6YCSHL.js → pt-TFZO5Y6T.js} +4 -1
- package/chunks/{qwenContentGenerator-K3HZ2H76.js → qwenContentGenerator-GXUZB5SJ.js} +28 -28
- package/chunks/{qwenOAuth2-PJQ27GSO.js → qwenOAuth2-LEYOU7RU.js} +5 -5
- package/chunks/{read-file-7SZSIZOL.js → read-file-I4OUZXTA.js} +8 -8
- package/chunks/{read-mcp-resource-SM5DA4RG.js → read-mcp-resource-JUFHCKSC.js} +2 -2
- package/chunks/{record-artifact-4XKZX3UW.js → record-artifact-DWSMBXYY.js} +2 -2
- package/chunks/{ripGrep-YNPFVCZP.js → ripGrep-XLCXANIO.js} +26 -26
- package/chunks/{ru-6BBHVZZV.js → ru-4L4LFHIT.js} +4 -1
- package/chunks/{run-qwen-serve-DMWV5N6L.js → run-qwen-serve-R6SBP6YS.js} +1232 -371
- package/chunks/{runtime-57WWRVHN.js → runtime-U3S33YUQ.js} +42 -37
- package/chunks/{scheduler-YBLGWSIL.js → scheduler-65QCIELP.js} +26 -26
- package/chunks/{send-message-F36YD6KQ.js → send-message-JZ752NWD.js} +3 -3
- package/chunks/{serve-R63NK3CV.js → serve-PSPOFCOD.js} +36 -34
- package/chunks/{server-Z22GCAXP.js → server-6P5ZB56O.js} +5561 -1377
- package/chunks/{session-HKAAI7BN.js → session-ILLOYW75.js} +298 -98
- package/chunks/{settings-3EYN66GK.js → settings-RMUJGYIK.js} +34 -32
- package/chunks/{shell-6YXINTWJ.js → shell-65GTAN5Q.js} +28 -26
- package/chunks/{skill-OO57P4X6.js → skill-L76U3YCX.js} +10 -10
- package/chunks/{spawnChannel-JD7G4VU6.js → spawnChannel-LXIGZ4EM.js} +28 -28
- package/chunks/{src-G5UPLZSZ.js → src-DWZT2X3Z.js} +87 -27
- package/chunks/{standalone-update-OB7F4JPZ.js → standalone-update-F6SRKPSD.js} +28 -28
- package/chunks/{startInteractiveUI-73EGLIIH.js → startInteractiveUI-UEJQP7S3.js} +730 -397
- package/chunks/{syntheticOutput-34H7LGA7.js → syntheticOutput-LELY6HAI.js} +3 -3
- package/chunks/{task-create-FRGOEIKY.js → task-create-QY3BIMNM.js} +8 -7
- package/chunks/{task-list-OW766BXZ.js → task-list-D2U47WBJ.js} +6 -5
- package/chunks/{task-stop-SIR5SNBN.js → task-stop-N6E5IYLH.js} +2 -2
- package/chunks/{task-update-YRA532MT.js → task-update-HJCQILLB.js} +8 -7
- package/chunks/{team-create-PECAAPXK.js → team-create-4QBZVOGN.js} +26 -26
- package/chunks/{team-delete-6OSECUZC.js → team-delete-RCWYDRF4.js} +6 -5
- package/chunks/{team-plan-approval-L6F434RY.js → team-plan-approval-BK4CIOFV.js} +26 -26
- package/chunks/{theme-manager-ZG4RY6GU.js → theme-manager-5F2IGW6E.js} +27 -27
- package/chunks/{todoWrite-YDMFEV4G.js → todoWrite-FKCGATPJ.js} +4 -4
- package/chunks/{tool-search-THVIWAPB.js → tool-search-J2ESO33H.js} +9 -9
- package/chunks/{total-session-admission-JOKNRIIA.js → total-session-admission-U4LZ5QTQ.js} +33 -33
- package/chunks/tree-sitter-YJVE2ZUK.js +2980 -0
- package/chunks/{trustedFolders-EOGT3UVS.js → trustedFolders-TIDOXCJA.js} +29 -28
- package/chunks/types-ML3TRJQ5.js +16 -0
- package/chunks/{updateCheck-2KYEYVSB.js → updateCheck-VRVR26FI.js} +29 -29
- package/chunks/{validateNonInterActiveAuth-EN6ZQSR2.js → validateNonInterActiveAuth-UGKYAVLG.js} +75 -71
- package/chunks/{version-3UEA6ZIR.js → version-7HR6VZAT.js} +1 -1
- package/chunks/{web-fetch-PAAEQKBW.js → web-fetch-QGODBB6S.js} +5 -5
- package/chunks/{workflow-QCCCMYP3.js → workflow-EDQAITEW.js} +27 -27
- package/chunks/workspace-providers-status-27UGAK77.js +73 -0
- package/chunks/workspace-registration-store-5H7577YQ.js +27 -0
- package/chunks/{workspace-registry-24QFLQYH.js → workspace-registry-F3THGAXC.js} +33 -33
- package/chunks/workspace-service-4JSAQAHP.js +86 -0
- package/chunks/workspace-skills-status-5JTWZHZU.js +72 -0
- package/chunks/{write-file-I3MUDRIE.js → write-file-UY6XCZZM.js} +27 -27
- package/chunks/{zh-RQE7I22T.js → zh-FSAH32JB.js} +6 -2
- package/chunks/{zh-TW-TBPQGHKH.js → zh-TW-D2YIE2LO.js} +6 -2
- package/cli.js +11 -11
- package/locales/ca.js +6 -0
- package/locales/de.js +6 -0
- package/locales/en.js +8 -0
- package/locales/fr.js +6 -0
- package/locales/ja.js +7 -1
- package/locales/pt.js +6 -0
- package/locales/ru.js +6 -0
- package/locales/zh-TW.js +9 -1
- package/locales/zh.js +9 -1
- package/package.json +3 -3
- package/web-shell/assets/{arc-C15hqnJe.js → arc-CObt_TK9.js} +1 -1
- package/web-shell/assets/{architectureDiagram-3BPJPVTR-B-At37J_.js → architectureDiagram-3BPJPVTR-B7aiJ6CK.js} +1 -1
- package/web-shell/assets/{blockDiagram-GPEHLZMM-B51wjcU7.js → blockDiagram-GPEHLZMM-BSK2CrID.js} +1 -1
- package/web-shell/assets/{c4Diagram-AAUBKEIU-xBK7BAwN.js → c4Diagram-AAUBKEIU-Bhx2p7T_.js} +1 -1
- package/web-shell/assets/channel-BBaLq8MV.js +1 -0
- package/web-shell/assets/{chunk-2J33WTMH-xKmLg7Yr.js → chunk-2J33WTMH-c3Hzv0Ni.js} +1 -1
- package/web-shell/assets/{chunk-4BX2VUAB-BfkdKZCM.js → chunk-4BX2VUAB-DNXaK7ZF.js} +1 -1
- package/web-shell/assets/{chunk-55IACEB6-DT713sZf.js → chunk-55IACEB6-BXjvDj_i.js} +1 -1
- package/web-shell/assets/{chunk-727SXJPM-CYEeAamv.js → chunk-727SXJPM-B6CaKFp-.js} +1 -1
- package/web-shell/assets/{chunk-AQP2D5EJ-DBN-5CFd.js → chunk-AQP2D5EJ-tij6Mz9B.js} +1 -1
- package/web-shell/assets/{chunk-FMBD7UC4-CGZKk4PH.js → chunk-FMBD7UC4-DdlsGk2h.js} +1 -1
- package/web-shell/assets/{chunk-ND2GUHAM-qQbC2O-y.js → chunk-ND2GUHAM-CdgGZSHE.js} +1 -1
- package/web-shell/assets/{chunk-QZHKN3VN-B3zeXwsX.js → chunk-QZHKN3VN-D8oJn1C0.js} +1 -1
- package/web-shell/assets/classDiagram-4FO5ZUOK-B2ZK6sWE.js +1 -0
- package/web-shell/assets/classDiagram-v2-Q7XG4LA2-B2ZK6sWE.js +1 -0
- package/web-shell/assets/{cose-bilkent-S5V4N54A-BKsl1Wvy.js → cose-bilkent-S5V4N54A-BUVmNp1Y.js} +1 -1
- package/web-shell/assets/{dagre-BM42HDAG-CuuuLiBK.js → dagre-BM42HDAG-CO4q310O.js} +1 -1
- package/web-shell/assets/{diagram-2AECGRRQ-CG4_eKiV.js → diagram-2AECGRRQ-C6gFvWkg.js} +1 -1
- package/web-shell/assets/{diagram-5GNKFQAL-DSFTmZB1.js → diagram-5GNKFQAL-NG2I_c0h.js} +1 -1
- package/web-shell/assets/{diagram-KO2AKTUF-BxwlEHn8.js → diagram-KO2AKTUF-DFKMS6I9.js} +1 -1
- package/web-shell/assets/{diagram-LMA3HP47-BUAg-jMG.js → diagram-LMA3HP47-BiElfsfP.js} +1 -1
- package/web-shell/assets/{diagram-OG6HWLK6-BFM2jlO5.js → diagram-OG6HWLK6-zLWDYUqu.js} +1 -1
- package/web-shell/assets/{erDiagram-TEJ5UH35-CerbFPa2.js → erDiagram-TEJ5UH35-DoVaazhK.js} +1 -1
- package/web-shell/assets/{flowDiagram-I6XJVG4X-BNeZz5Zz.js → flowDiagram-I6XJVG4X-0RLz-mXg.js} +1 -1
- package/web-shell/assets/{ganttDiagram-6RSMTGT7-CTOogLaM.js → ganttDiagram-6RSMTGT7-C-eX1A39.js} +3 -3
- package/web-shell/assets/{gitGraphDiagram-PVQCEYII-yxSFkXPj.js → gitGraphDiagram-PVQCEYII-BauQu1Bu.js} +1 -1
- package/web-shell/assets/index-3uAT3ehO.css +5 -0
- package/web-shell/assets/index-CVHa2QH7.js +3 -0
- package/web-shell/assets/index-D1KigWat.js +1128 -0
- package/web-shell/assets/{infoDiagram-5YYISTIA-DYdAUevm.js → infoDiagram-5YYISTIA-C800cFvZ.js} +1 -1
- package/web-shell/assets/{ishikawaDiagram-YF4QCWOH-CNuijZ90.js → ishikawaDiagram-YF4QCWOH-CFK1Jk6e.js} +1 -1
- package/web-shell/assets/{journeyDiagram-JHISSGLW-BTyiuonf.js → journeyDiagram-JHISSGLW-Cjx0WAC4.js} +1 -1
- package/web-shell/assets/{kanban-definition-UN3LZRKU-DPJo0Uow.js → kanban-definition-UN3LZRKU-DWMDHbiv.js} +1 -1
- package/web-shell/assets/{linear-C8wKCbIC.js → linear-vpW4FrZs.js} +1 -1
- package/web-shell/assets/{mermaid.core-BV5o7nW5.js → mermaid.core-DsHRUnDd.js} +5 -5
- package/web-shell/assets/{mindmap-definition-RKZ34NQL-BtLcBmSt.js → mindmap-definition-RKZ34NQL-D3Eb1cAK.js} +1 -1
- package/web-shell/assets/{pieDiagram-4H26LBE5-C22J7A9Q.js → pieDiagram-4H26LBE5-Cr2-6sWt.js} +1 -1
- package/web-shell/assets/{quadrantDiagram-W4KKPZXB-DSL10wiL.js → quadrantDiagram-W4KKPZXB-Yd7yUgVp.js} +1 -1
- package/web-shell/assets/{requirementDiagram-4Y6WPE33-feqh0UpX.js → requirementDiagram-4Y6WPE33-BE6SprL8.js} +1 -1
- package/web-shell/assets/{sankeyDiagram-5OEKKPKP-Z4OVaWaZ.js → sankeyDiagram-5OEKKPKP-DzkuMK8d.js} +1 -1
- package/web-shell/assets/{sequenceDiagram-3UESZ5HK-DIluIcb1.js → sequenceDiagram-3UESZ5HK-wD9A3Dhi.js} +1 -1
- package/web-shell/assets/{stateDiagram-AJRCARHV-hlhnuoYH.js → stateDiagram-AJRCARHV-BOp5q1Nm.js} +1 -1
- package/web-shell/assets/stateDiagram-v2-BHNVJYJU-BDsWUmPG.js +1 -0
- package/web-shell/assets/{timeline-definition-PNZ67QCA-Dqf_CqgL.js → timeline-definition-PNZ67QCA-Btc08sUA.js} +1 -1
- package/web-shell/assets/{vennDiagram-CIIHVFJN-DD2QhaTX.js → vennDiagram-CIIHVFJN-Bv6upsOA.js} +1 -1
- package/web-shell/assets/{wardley-L42UT6IY-B2uI85U_.js → wardley-L42UT6IY-D-XamnCK.js} +1 -1
- package/web-shell/assets/{wardleyDiagram-YWT4CUSO-U1aJX71g.js → wardleyDiagram-YWT4CUSO-BbP_5Wfp.js} +1 -1
- package/web-shell/assets/{xychartDiagram-2RQKCTM6-C0ODHUee.js → xychartDiagram-2RQKCTM6-CO5Hh28b.js} +1 -1
- package/web-shell/index.html +2 -2
- package/chunks/chunk-CU3L64TP.js +0 -93
- package/chunks/initializer-UTQ7FB7G.js +0 -71
- package/chunks/list-DEICDL65.js +0 -74
- package/chunks/loadedSettingsAdapter-NOWEGDYJ.js +0 -68
- package/chunks/mcp-L5JXEEIA.js +0 -68
- package/chunks/nonInteractiveCli-LS2K4X6A.js +0 -124
- package/chunks/types-JNKGKUJT.js +0 -12
- package/chunks/workspace-providers-status-4KMGETOJ.js +0 -71
- package/chunks/workspace-service-RVLBMSMZ.js +0 -80
- package/chunks/workspace-skills-status-DS4ZVFTO.js +0 -70
- package/web-shell/assets/channel-CLiQD50f.js +0 -1
- package/web-shell/assets/classDiagram-4FO5ZUOK-D3sYqow5.js +0 -1
- package/web-shell/assets/classDiagram-v2-Q7XG4LA2-D3sYqow5.js +0 -1
- package/web-shell/assets/index-D5IWFVk1.css +0 -5
- package/web-shell/assets/index-KFJRDhX7.js +0 -783
- package/web-shell/assets/stateDiagram-v2-BHNVJYJU-BhQMwgpl.js +0 -1
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
import {
|
|
4
4
|
formatHistoryGapNotice,
|
|
5
5
|
indexGapsByChild
|
|
6
|
-
} from "./chunk-
|
|
6
|
+
} from "./chunk-ZEEVKDO7.js";
|
|
7
7
|
import {
|
|
8
8
|
require_undici
|
|
9
9
|
} from "./chunk-L573Z2QZ.js";
|
|
@@ -12,13 +12,16 @@ import {
|
|
|
12
12
|
} from "./chunk-YUZI3WAC.js";
|
|
13
13
|
import {
|
|
14
14
|
getActiveGoal
|
|
15
|
-
} from "./chunk-
|
|
15
|
+
} from "./chunk-XCR44EEA.js";
|
|
16
|
+
import {
|
|
17
|
+
apiActivityTracker
|
|
18
|
+
} from "./chunk-BYOOK22F.js";
|
|
16
19
|
import {
|
|
17
20
|
ToolNames
|
|
18
|
-
} from "./chunk-
|
|
21
|
+
} from "./chunk-JGUT3LWZ.js";
|
|
19
22
|
import {
|
|
20
23
|
createDebugLogger
|
|
21
|
-
} from "./chunk-
|
|
24
|
+
} from "./chunk-7NFB7LBQ.js";
|
|
22
25
|
import {
|
|
23
26
|
init_esbuild_shims
|
|
24
27
|
} from "./chunk-5O2XNYP6.js";
|
|
@@ -27,1435 +30,1467 @@ import {
|
|
|
27
30
|
__toESM
|
|
28
31
|
} from "./chunk-J2S4EL5Y.js";
|
|
29
32
|
|
|
30
|
-
// packages/cli/src/
|
|
31
|
-
init_esbuild_shims();
|
|
32
|
-
|
|
33
|
-
// packages/cli/src/acp-integration/session/emitters/MessageEmitter.ts
|
|
33
|
+
// packages/cli/src/services/setup-github.ts
|
|
34
34
|
init_esbuild_shims();
|
|
35
|
+
var import_undici2 = __toESM(require_undici(), 1);
|
|
36
|
+
import { promises as fsp } from "node:fs";
|
|
37
|
+
import * as path from "node:path";
|
|
35
38
|
|
|
36
|
-
// packages/cli/src/
|
|
39
|
+
// packages/cli/src/utils/gitUtils.ts
|
|
37
40
|
init_esbuild_shims();
|
|
38
|
-
var
|
|
39
|
-
|
|
40
|
-
|
|
41
|
+
var import_undici = __toESM(require_undici(), 1);
|
|
42
|
+
import * as childProcess from "node:child_process";
|
|
43
|
+
var debugLogger = createDebugLogger("GIT");
|
|
44
|
+
async function runGit(args, opts = {}) {
|
|
45
|
+
return await new Promise((resolve2, reject) => {
|
|
46
|
+
childProcess.execFile(
|
|
47
|
+
"git",
|
|
48
|
+
args,
|
|
49
|
+
{
|
|
50
|
+
encoding: "utf-8",
|
|
51
|
+
...opts.cwd ? { cwd: opts.cwd } : {}
|
|
52
|
+
},
|
|
53
|
+
(err, stdout) => {
|
|
54
|
+
if (err) {
|
|
55
|
+
reject(err);
|
|
56
|
+
return;
|
|
57
|
+
}
|
|
58
|
+
resolve2(String(stdout ?? "").trim());
|
|
59
|
+
}
|
|
60
|
+
);
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
__name(runGit, "runGit");
|
|
64
|
+
var isGitHubRepositoryAsync = /* @__PURE__ */ __name(async (opts = {}) => {
|
|
65
|
+
try {
|
|
66
|
+
const remotes = await runGit(["remote", "-v"], opts);
|
|
67
|
+
return remotes.split("\n").some((line) => {
|
|
68
|
+
const remoteUrl = line.trim().split(/\s+/)[1];
|
|
69
|
+
return remoteUrl ? isGitHubRemoteUrl(remoteUrl) : false;
|
|
70
|
+
});
|
|
71
|
+
} catch (_error) {
|
|
72
|
+
debugLogger.debug(`Failed to get git remote:`, _error);
|
|
73
|
+
return false;
|
|
41
74
|
}
|
|
42
|
-
|
|
43
|
-
|
|
75
|
+
}, "isGitHubRepositoryAsync");
|
|
76
|
+
function isGitHubRemoteUrl(remoteUrl) {
|
|
77
|
+
if (remoteUrl.startsWith("git@github.com:")) {
|
|
78
|
+
return true;
|
|
44
79
|
}
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
80
|
+
if (remoteUrl.startsWith("git@")) {
|
|
81
|
+
return false;
|
|
82
|
+
}
|
|
83
|
+
try {
|
|
84
|
+
return new URL(remoteUrl).hostname === "github.com";
|
|
85
|
+
} catch {
|
|
86
|
+
return false;
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
__name(isGitHubRemoteUrl, "isGitHubRemoteUrl");
|
|
90
|
+
var getGitRepoRootAsync = /* @__PURE__ */ __name(async (opts = {}) => {
|
|
91
|
+
const gitRepoRoot = await runGit(["rev-parse", "--show-toplevel"], opts);
|
|
92
|
+
if (!gitRepoRoot) {
|
|
93
|
+
throw new Error(`Git repo returned empty value`);
|
|
94
|
+
}
|
|
95
|
+
return gitRepoRoot;
|
|
96
|
+
}, "getGitRepoRootAsync");
|
|
97
|
+
var getLatestGitHubRelease = /* @__PURE__ */ __name(async (proxy) => {
|
|
98
|
+
try {
|
|
99
|
+
const controller = new AbortController();
|
|
100
|
+
const endpoint = `https://api.github.com/repos/QwenLM/qwen-code-action/releases/latest`;
|
|
101
|
+
const response = await fetch(endpoint, {
|
|
102
|
+
method: "GET",
|
|
103
|
+
headers: {
|
|
104
|
+
Accept: "application/vnd.github+json",
|
|
105
|
+
"Content-Type": "application/json",
|
|
106
|
+
"X-GitHub-Api-Version": "2022-11-28"
|
|
107
|
+
},
|
|
108
|
+
dispatcher: proxy ? new import_undici.ProxyAgent(proxy) : void 0,
|
|
109
|
+
signal: AbortSignal.any([AbortSignal.timeout(3e4), controller.signal])
|
|
110
|
+
});
|
|
111
|
+
if (!response.ok) {
|
|
112
|
+
throw new Error(
|
|
113
|
+
`Invalid response code: ${response.status} - ${response.statusText}`
|
|
114
|
+
);
|
|
52
115
|
}
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
116
|
+
const releaseTag = (await response.json()).tag_name;
|
|
117
|
+
if (!releaseTag) {
|
|
118
|
+
throw new Error(`Response did not include tag_name field`);
|
|
56
119
|
}
|
|
57
|
-
return
|
|
120
|
+
return releaseTag;
|
|
121
|
+
} catch (_error) {
|
|
122
|
+
debugLogger.debug(
|
|
123
|
+
`Failed to determine latest qwen-code-action release:`,
|
|
124
|
+
_error
|
|
125
|
+
);
|
|
126
|
+
throw new Error(
|
|
127
|
+
`Unable to determine the latest qwen-code-action release on GitHub.`
|
|
128
|
+
);
|
|
58
129
|
}
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
130
|
+
}, "getLatestGitHubRelease");
|
|
131
|
+
async function getGitHubRepoInfoAsync(opts = {}) {
|
|
132
|
+
return parseGitHubRepoInfo(
|
|
133
|
+
await runGit(["remote", "get-url", "origin"], opts)
|
|
134
|
+
);
|
|
135
|
+
}
|
|
136
|
+
__name(getGitHubRepoInfoAsync, "getGitHubRepoInfoAsync");
|
|
137
|
+
function parseGitHubRepoInfo(remoteUrl) {
|
|
138
|
+
let urlToParse = remoteUrl;
|
|
139
|
+
if (remoteUrl.startsWith("git@github.com:")) {
|
|
140
|
+
urlToParse = remoteUrl.replace("git@github.com:", "");
|
|
141
|
+
} else if (remoteUrl.startsWith("git@")) {
|
|
142
|
+
throw new Error(
|
|
143
|
+
`Owner & repo could not be extracted from remote URL: ${remoteUrl}`
|
|
144
|
+
);
|
|
69
145
|
}
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
146
|
+
let parsedUrl;
|
|
147
|
+
try {
|
|
148
|
+
parsedUrl = new URL(urlToParse, "https://github.com");
|
|
149
|
+
} catch {
|
|
150
|
+
throw new Error(
|
|
151
|
+
`Owner & repo could not be extracted from remote URL: ${remoteUrl}`
|
|
152
|
+
);
|
|
75
153
|
}
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
return this.ctx.sessionId;
|
|
154
|
+
if (parsedUrl.hostname !== "github.com") {
|
|
155
|
+
throw new Error(
|
|
156
|
+
`Owner & repo could not be extracted from remote URL: ${remoteUrl}`
|
|
157
|
+
);
|
|
81
158
|
}
|
|
82
|
-
|
|
159
|
+
const parts = parsedUrl.pathname.split("/").filter((part) => part !== "");
|
|
160
|
+
if (parts.length !== 2 || !parts[0] || !parts[1]) {
|
|
161
|
+
throw new Error(
|
|
162
|
+
`Owner & repo could not be extracted from remote URL: ${remoteUrl}`
|
|
163
|
+
);
|
|
164
|
+
}
|
|
165
|
+
return { owner: parts[0], repo: parts[1].replace(/\.git$/, "") };
|
|
166
|
+
}
|
|
167
|
+
__name(parseGitHubRepoInfo, "parseGitHubRepoInfo");
|
|
83
168
|
|
|
84
|
-
// packages/cli/src/
|
|
85
|
-
var
|
|
169
|
+
// packages/cli/src/services/setup-github.ts
|
|
170
|
+
var debugLogger2 = createDebugLogger("SETUP_GITHUB");
|
|
171
|
+
var GITHUB_WORKFLOW_PATHS = [
|
|
172
|
+
"qwen-dispatch/qwen-dispatch.yml",
|
|
173
|
+
"qwen-assistant/qwen-invoke.yml",
|
|
174
|
+
"issue-triage/qwen-triage.yml",
|
|
175
|
+
"issue-triage/qwen-scheduled-triage.yml",
|
|
176
|
+
"pr-review/qwen-review.yml"
|
|
177
|
+
];
|
|
178
|
+
var GITIGNORE_ENTRIES = [".qwen/", "gha-creds-*.json"];
|
|
179
|
+
var MAX_WORKFLOW_DOWNLOAD_BYTES = 5 * 1024 * 1024;
|
|
180
|
+
var SetupGithubError = class extends Error {
|
|
86
181
|
static {
|
|
87
|
-
__name(this, "
|
|
182
|
+
__name(this, "SetupGithubError");
|
|
88
183
|
}
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
sessionUpdate: "agent_message_chunk",
|
|
101
|
-
content: { type: "text", text: "" },
|
|
102
|
-
_meta: {
|
|
103
|
-
stopHookLoop: {
|
|
104
|
-
iterationCount,
|
|
105
|
-
reasons,
|
|
106
|
-
stopHookCount,
|
|
107
|
-
...activeGoal ? {
|
|
108
|
-
goal: {
|
|
109
|
-
condition: activeGoal.condition,
|
|
110
|
-
iterations: activeGoal.iterations,
|
|
111
|
-
setAt: activeGoal.setAt,
|
|
112
|
-
lastReason: activeGoal.lastReason
|
|
113
|
-
}
|
|
114
|
-
} : {}
|
|
115
|
-
}
|
|
116
|
-
}
|
|
117
|
-
});
|
|
184
|
+
code;
|
|
185
|
+
status;
|
|
186
|
+
partial;
|
|
187
|
+
partialResult;
|
|
188
|
+
constructor(code, message, status, partialResult) {
|
|
189
|
+
super(message);
|
|
190
|
+
this.name = "SetupGithubError";
|
|
191
|
+
this.code = code;
|
|
192
|
+
this.status = status;
|
|
193
|
+
this.partial = partialResult !== void 0;
|
|
194
|
+
this.partialResult = partialResult;
|
|
118
195
|
}
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
196
|
+
};
|
|
197
|
+
var nodeFileOps = {
|
|
198
|
+
assertCanWrite() {
|
|
199
|
+
},
|
|
200
|
+
async ensureWorkflowDirectory(gitRepoRoot) {
|
|
201
|
+
await fsp.mkdir(path.join(gitRepoRoot, ".github", "workflows"), {
|
|
202
|
+
recursive: true
|
|
126
203
|
});
|
|
127
|
-
}
|
|
128
|
-
async
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
204
|
+
},
|
|
205
|
+
async writeTextFile(gitRepoRoot, relativePath, content) {
|
|
206
|
+
const target = path.join(gitRepoRoot, relativePath);
|
|
207
|
+
await fsp.writeFile(target, content, { mode: 420 });
|
|
208
|
+
return { sizeBytes: Buffer.byteLength(content, "utf8") };
|
|
209
|
+
},
|
|
210
|
+
async readTextFile(gitRepoRoot, relativePath) {
|
|
211
|
+
try {
|
|
212
|
+
return await fsp.readFile(path.join(gitRepoRoot, relativePath), "utf8");
|
|
213
|
+
} catch (error) {
|
|
214
|
+
if (error.code === "ENOENT") {
|
|
215
|
+
return void 0;
|
|
134
216
|
}
|
|
135
|
-
|
|
217
|
+
throw error;
|
|
218
|
+
}
|
|
136
219
|
}
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
...epochMs != null ? { timestamp: epochMs } : {},
|
|
147
|
-
...options.source ? { source: options.source } : {}
|
|
148
|
-
};
|
|
149
|
-
await this.sendUpdate({
|
|
150
|
-
sessionUpdate: "user_message_chunk",
|
|
151
|
-
content: { type: "text", text },
|
|
152
|
-
...Object.keys(_meta).length > 0 ? { _meta } : {}
|
|
153
|
-
});
|
|
154
|
-
}
|
|
155
|
-
/**
|
|
156
|
-
* Emits an agent thought chunk.
|
|
157
|
-
*
|
|
158
|
-
* @param text - The thought text content
|
|
159
|
-
* @param timestamp - Optional server-side timestamp (ISO string or ms) for message ordering
|
|
160
|
-
*/
|
|
161
|
-
async emitAgentThought(text, timestamp, subagentMeta) {
|
|
162
|
-
const _meta = this.buildChunkMeta(
|
|
163
|
-
BaseEmitter.toEpochMs(timestamp),
|
|
164
|
-
subagentMeta
|
|
220
|
+
};
|
|
221
|
+
async function setupGithub(options = {}) {
|
|
222
|
+
const cwd = options.cwd ?? process.cwd();
|
|
223
|
+
const fileOps = options.fileOps ?? nodeFileOps;
|
|
224
|
+
if (!await isGitHubRepositoryAsync({ cwd })) {
|
|
225
|
+
throw new SetupGithubError(
|
|
226
|
+
"github_repository_not_found",
|
|
227
|
+
"Unable to determine the GitHub repository. /setup-github must be run from a git repository.",
|
|
228
|
+
400
|
|
165
229
|
);
|
|
166
|
-
await this.sendUpdate({
|
|
167
|
-
sessionUpdate: "agent_thought_chunk",
|
|
168
|
-
content: { type: "text", text },
|
|
169
|
-
..._meta ? { _meta } : {}
|
|
170
|
-
});
|
|
171
230
|
}
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
subagentMeta
|
|
231
|
+
let gitRepoRoot;
|
|
232
|
+
try {
|
|
233
|
+
gitRepoRoot = await getGitRepoRootAsync({ cwd });
|
|
234
|
+
} catch (error) {
|
|
235
|
+
debugLogger2.debug("Failed to get git repo root:", error);
|
|
236
|
+
throw new SetupGithubError(
|
|
237
|
+
"github_repository_not_found",
|
|
238
|
+
"Unable to determine the GitHub repository. /setup-github must be run from a git repository.",
|
|
239
|
+
400
|
|
182
240
|
);
|
|
183
|
-
await this.sendUpdate({
|
|
184
|
-
sessionUpdate: "agent_message_chunk",
|
|
185
|
-
content: { type: "text", text },
|
|
186
|
-
..._meta ? { _meta } : {}
|
|
187
|
-
});
|
|
188
241
|
}
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
};
|
|
200
|
-
const cumulative = this.ctx.cumulativeUsage;
|
|
201
|
-
if (cumulative) {
|
|
202
|
-
const addFinite = /* @__PURE__ */ __name((total, value) => typeof value === "number" && Number.isFinite(value) ? total + value : total, "addFinite");
|
|
203
|
-
cumulative.promptTokens = addFinite(
|
|
204
|
-
cumulative.promptTokens,
|
|
205
|
-
usage.inputTokens
|
|
242
|
+
if (options.workspaceRoot) {
|
|
243
|
+
const [gitRootReal, workspaceReal] = await Promise.all([
|
|
244
|
+
realpathOrResolve(gitRepoRoot),
|
|
245
|
+
realpathOrResolve(options.workspaceRoot)
|
|
246
|
+
]);
|
|
247
|
+
if (gitRootReal !== workspaceReal) {
|
|
248
|
+
throw new SetupGithubError(
|
|
249
|
+
"github_git_root_mismatch",
|
|
250
|
+
"The Git repository root must match the daemon workspace root.",
|
|
251
|
+
400
|
|
206
252
|
);
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
fileOps.assertCanWrite?.();
|
|
256
|
+
let releaseTag;
|
|
257
|
+
try {
|
|
258
|
+
releaseTag = await getLatestGitHubRelease(options.proxy);
|
|
259
|
+
} catch (error) {
|
|
260
|
+
writeStderrLine(
|
|
261
|
+
`qwen setup-github: failed to determine latest qwen-code-action release: ${error instanceof Error ? error.message : String(error)}`
|
|
262
|
+
);
|
|
263
|
+
debugLogger2.debug(
|
|
264
|
+
"Failed to determine latest qwen-code-action release:",
|
|
265
|
+
error
|
|
266
|
+
);
|
|
267
|
+
throw new SetupGithubError(
|
|
268
|
+
"github_release_lookup_failed",
|
|
269
|
+
"Unable to determine the latest qwen-code-action release on GitHub.",
|
|
270
|
+
502
|
|
271
|
+
);
|
|
272
|
+
}
|
|
273
|
+
const readmeUrl = `https://github.com/QwenLM/qwen-code-action/blob/${releaseTag}/README.md#quick-start`;
|
|
274
|
+
const secretsUrl = await resolveSecretsUrl(cwd);
|
|
275
|
+
const downloads = await downloadWorkflows({
|
|
276
|
+
releaseTag,
|
|
277
|
+
proxy: options.proxy,
|
|
278
|
+
abortSignal: options.abortSignal,
|
|
279
|
+
fetchImpl: options.fetchImpl ?? fetch
|
|
280
|
+
});
|
|
281
|
+
const result = {
|
|
282
|
+
kind: "github_setup",
|
|
283
|
+
workspaceCwd: options.workspaceRoot ?? gitRepoRoot,
|
|
284
|
+
gitRepoRoot,
|
|
285
|
+
releaseTag,
|
|
286
|
+
readmeUrl,
|
|
287
|
+
...secretsUrl ? { secretsUrl } : {},
|
|
288
|
+
workflows: [],
|
|
289
|
+
gitignore: { path: ".gitignore", status: "skipped" },
|
|
290
|
+
warnings: []
|
|
291
|
+
};
|
|
292
|
+
result.gitignore = await updateGitignore(gitRepoRoot, fileOps);
|
|
293
|
+
if (result.gitignore.status === "failed") {
|
|
294
|
+
result.warnings.push("Failed to update .gitignore.");
|
|
295
|
+
}
|
|
296
|
+
try {
|
|
297
|
+
await fileOps.ensureWorkflowDirectory(gitRepoRoot);
|
|
298
|
+
for (const workflow of downloads) {
|
|
299
|
+
const relativePath = path.posix.join(
|
|
300
|
+
".github",
|
|
301
|
+
"workflows",
|
|
302
|
+
path.posix.basename(workflow.sourcePath)
|
|
210
303
|
);
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
304
|
+
try {
|
|
305
|
+
const write = await fileOps.writeTextFile(
|
|
306
|
+
gitRepoRoot,
|
|
307
|
+
relativePath,
|
|
308
|
+
workflow.content
|
|
309
|
+
);
|
|
310
|
+
result.workflows.push({
|
|
311
|
+
sourcePath: workflow.sourcePath,
|
|
312
|
+
path: relativePath,
|
|
313
|
+
status: "written",
|
|
314
|
+
sizeBytes: write.sizeBytes
|
|
315
|
+
});
|
|
316
|
+
} catch (error) {
|
|
317
|
+
result.partial = true;
|
|
318
|
+
result.workflows.push({
|
|
319
|
+
sourcePath: workflow.sourcePath,
|
|
320
|
+
path: relativePath,
|
|
321
|
+
status: "failed",
|
|
322
|
+
error: error instanceof Error ? error.message : String(error)
|
|
323
|
+
});
|
|
324
|
+
throw new SetupGithubError(
|
|
325
|
+
"github_workflow_write_failed",
|
|
326
|
+
`Unable to write ${relativePath}.`,
|
|
327
|
+
500,
|
|
328
|
+
result
|
|
329
|
+
);
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
} catch (error) {
|
|
333
|
+
if (error instanceof SetupGithubError) throw error;
|
|
334
|
+
throw new SetupGithubError(
|
|
335
|
+
"github_workflow_write_failed",
|
|
336
|
+
"Unable to create .github/workflows.",
|
|
337
|
+
500,
|
|
338
|
+
result
|
|
339
|
+
);
|
|
340
|
+
}
|
|
341
|
+
return result;
|
|
342
|
+
}
|
|
343
|
+
__name(setupGithub, "setupGithub");
|
|
344
|
+
async function updateGitignore(gitRepoRoot, fileOps = nodeFileOps) {
|
|
345
|
+
try {
|
|
346
|
+
const existingContent = await fileOps.readTextFile(
|
|
347
|
+
gitRepoRoot,
|
|
348
|
+
".gitignore"
|
|
349
|
+
);
|
|
350
|
+
if (existingContent === void 0) {
|
|
351
|
+
const content = GITIGNORE_ENTRIES.join("\n") + "\n";
|
|
352
|
+
await fileOps.writeTextFile(gitRepoRoot, ".gitignore", content);
|
|
353
|
+
return {
|
|
354
|
+
path: ".gitignore",
|
|
355
|
+
status: "created",
|
|
356
|
+
added: [...GITIGNORE_ENTRIES]
|
|
357
|
+
};
|
|
358
|
+
}
|
|
359
|
+
const missingEntries = GITIGNORE_ENTRIES.filter(
|
|
360
|
+
(entry) => !existingContent.split(/\r?\n/).some((line) => line.split("#")[0].trim() === entry)
|
|
361
|
+
);
|
|
362
|
+
if (missingEntries.length === 0) {
|
|
363
|
+
return { path: ".gitignore", status: "unchanged" };
|
|
364
|
+
}
|
|
365
|
+
const nextContent = existingContent + "\n" + missingEntries.join("\n") + "\n";
|
|
366
|
+
await fileOps.writeTextFile(gitRepoRoot, ".gitignore", nextContent);
|
|
367
|
+
return {
|
|
368
|
+
path: ".gitignore",
|
|
369
|
+
status: "updated",
|
|
370
|
+
added: missingEntries
|
|
371
|
+
};
|
|
372
|
+
} catch (error) {
|
|
373
|
+
debugLogger2.debug("Failed to update .gitignore:", error);
|
|
374
|
+
return {
|
|
375
|
+
path: ".gitignore",
|
|
376
|
+
status: "failed",
|
|
377
|
+
error: error instanceof Error ? error.message : String(error)
|
|
378
|
+
};
|
|
379
|
+
}
|
|
380
|
+
}
|
|
381
|
+
__name(updateGitignore, "updateGitignore");
|
|
382
|
+
async function downloadWorkflows(options) {
|
|
383
|
+
const internalAbort = new AbortController();
|
|
384
|
+
try {
|
|
385
|
+
const dispatcher = options.proxy ? new import_undici2.ProxyAgent(options.proxy) : void 0;
|
|
386
|
+
return await Promise.all(
|
|
387
|
+
GITHUB_WORKFLOW_PATHS.map(async (workflow) => {
|
|
388
|
+
const endpoint = `https://raw.githubusercontent.com/QwenLM/qwen-code-action/refs/tags/${options.releaseTag}/examples/workflows/${workflow}`;
|
|
389
|
+
const response = await options.fetchImpl(endpoint, {
|
|
390
|
+
method: "GET",
|
|
391
|
+
dispatcher,
|
|
392
|
+
signal: AbortSignal.any([
|
|
393
|
+
AbortSignal.timeout(3e4),
|
|
394
|
+
internalAbort.signal,
|
|
395
|
+
...options.abortSignal ? [options.abortSignal] : []
|
|
396
|
+
])
|
|
397
|
+
});
|
|
398
|
+
if (!response.ok) {
|
|
399
|
+
throw new Error(
|
|
400
|
+
`Invalid response code downloading ${endpoint}: ${response.status} - ${response.statusText}`
|
|
401
|
+
);
|
|
402
|
+
}
|
|
403
|
+
return {
|
|
404
|
+
sourcePath: workflow,
|
|
405
|
+
content: await readResponseTextWithLimit(response, workflow)
|
|
406
|
+
};
|
|
407
|
+
})
|
|
408
|
+
);
|
|
409
|
+
} catch (error) {
|
|
410
|
+
internalAbort.abort();
|
|
411
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
412
|
+
debugLogger2.debug("Failed to download qwen-code-action workflows:", error);
|
|
413
|
+
throw new SetupGithubError(
|
|
414
|
+
"github_workflow_download_failed",
|
|
415
|
+
`Unable to download qwen-code-action workflows from GitHub. ${message}`,
|
|
416
|
+
502
|
|
417
|
+
);
|
|
418
|
+
}
|
|
419
|
+
}
|
|
420
|
+
__name(downloadWorkflows, "downloadWorkflows");
|
|
421
|
+
async function readResponseTextWithLimit(response, sourcePath) {
|
|
422
|
+
const contentLength = response.headers.get("content-length");
|
|
423
|
+
if (contentLength !== null) {
|
|
424
|
+
const parsedLength = Number(contentLength);
|
|
425
|
+
if (Number.isFinite(parsedLength) && parsedLength > MAX_WORKFLOW_DOWNLOAD_BYTES) {
|
|
426
|
+
throw new Error(
|
|
427
|
+
`${sourcePath} exceeds download limit of ${MAX_WORKFLOW_DOWNLOAD_BYTES} bytes`
|
|
214
428
|
);
|
|
215
|
-
cumulative.apiTimeMs = addFinite(cumulative.apiTimeMs, durationMs);
|
|
216
429
|
}
|
|
217
|
-
const meta = typeof durationMs === "number" ? { usage, durationMs, ...subagentMeta } : { usage, ...subagentMeta };
|
|
218
|
-
await this.sendUpdate({
|
|
219
|
-
sessionUpdate: "agent_message_chunk",
|
|
220
|
-
content: { type: "text", text },
|
|
221
|
-
_meta: meta
|
|
222
|
-
});
|
|
223
430
|
}
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
431
|
+
if (!response.body) return "";
|
|
432
|
+
const reader = response.body.getReader();
|
|
433
|
+
const chunks = [];
|
|
434
|
+
let totalBytes = 0;
|
|
435
|
+
try {
|
|
436
|
+
while (true) {
|
|
437
|
+
const { done, value } = await reader.read();
|
|
438
|
+
if (done) break;
|
|
439
|
+
if (!value) continue;
|
|
440
|
+
totalBytes += value.byteLength;
|
|
441
|
+
if (totalBytes > MAX_WORKFLOW_DOWNLOAD_BYTES) {
|
|
442
|
+
await reader.cancel().catch(() => {
|
|
443
|
+
});
|
|
444
|
+
throw new Error(
|
|
445
|
+
`${sourcePath} exceeds download limit of ${MAX_WORKFLOW_DOWNLOAD_BYTES} bytes`
|
|
446
|
+
);
|
|
447
|
+
}
|
|
448
|
+
chunks.push(value);
|
|
236
449
|
}
|
|
237
|
-
|
|
450
|
+
} finally {
|
|
451
|
+
reader.releaseLock();
|
|
238
452
|
}
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
};
|
|
245
|
-
return Object.keys(meta).length > 0 ? meta : void 0;
|
|
453
|
+
const body = new Uint8Array(totalBytes);
|
|
454
|
+
let offset = 0;
|
|
455
|
+
for (const chunk of chunks) {
|
|
456
|
+
body.set(chunk, offset);
|
|
457
|
+
offset += chunk.byteLength;
|
|
246
458
|
}
|
|
247
|
-
|
|
459
|
+
return new TextDecoder().decode(body);
|
|
460
|
+
}
|
|
461
|
+
__name(readResponseTextWithLimit, "readResponseTextWithLimit");
|
|
462
|
+
async function resolveSecretsUrl(cwd) {
|
|
463
|
+
try {
|
|
464
|
+
const repoInfo = await getGitHubRepoInfoAsync({ cwd });
|
|
465
|
+
return `https://github.com/${repoInfo.owner}/${repoInfo.repo}/settings/secrets/actions`;
|
|
466
|
+
} catch {
|
|
467
|
+
return void 0;
|
|
468
|
+
}
|
|
469
|
+
}
|
|
470
|
+
__name(resolveSecretsUrl, "resolveSecretsUrl");
|
|
471
|
+
async function realpathOrResolve(input) {
|
|
472
|
+
try {
|
|
473
|
+
return await fsp.realpath(input);
|
|
474
|
+
} catch {
|
|
475
|
+
return path.resolve(input);
|
|
476
|
+
}
|
|
477
|
+
}
|
|
478
|
+
__name(realpathOrResolve, "realpathOrResolve");
|
|
248
479
|
|
|
249
|
-
// packages/cli/src/acp-integration/session/emitters/
|
|
480
|
+
// packages/cli/src/acp-integration/session/emitters/MessageEmitter.ts
|
|
250
481
|
init_esbuild_shims();
|
|
251
482
|
|
|
252
|
-
// packages/cli/src/acp-integration/session/emitters/
|
|
483
|
+
// packages/cli/src/acp-integration/session/emitters/base-emitter.ts
|
|
253
484
|
init_esbuild_shims();
|
|
254
|
-
var
|
|
485
|
+
var BaseEmitter = class {
|
|
486
|
+
constructor(ctx) {
|
|
487
|
+
this.ctx = ctx;
|
|
488
|
+
}
|
|
255
489
|
static {
|
|
256
|
-
__name(this, "
|
|
490
|
+
__name(this, "BaseEmitter");
|
|
257
491
|
}
|
|
258
492
|
/**
|
|
259
|
-
*
|
|
260
|
-
*
|
|
261
|
-
* @param todos - Array of todo items to send as plan entries
|
|
493
|
+
* Converts an ISO timestamp string or epoch ms to epoch ms number.
|
|
494
|
+
* Returns undefined if the input is not a valid timestamp.
|
|
262
495
|
*/
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
sessionUpdate: "plan",
|
|
273
|
-
entries,
|
|
274
|
-
...cumulative ? { _meta: { stats: { ...cumulative } } } : {}
|
|
275
|
-
});
|
|
496
|
+
static toEpochMs(ts) {
|
|
497
|
+
if (typeof ts === "number") {
|
|
498
|
+
return Number.isFinite(ts) ? ts : void 0;
|
|
499
|
+
}
|
|
500
|
+
if (typeof ts === "string") {
|
|
501
|
+
const ms = new Date(ts).getTime();
|
|
502
|
+
return Number.isFinite(ms) ? ms : void 0;
|
|
503
|
+
}
|
|
504
|
+
return void 0;
|
|
276
505
|
}
|
|
277
506
|
/**
|
|
278
|
-
*
|
|
279
|
-
*
|
|
280
|
-
*
|
|
281
|
-
* 2. Result display as JSON string
|
|
282
|
-
* 3. Args with 'todos' array
|
|
283
|
-
*
|
|
284
|
-
* @param resultDisplay - The tool result display (object, string, or undefined)
|
|
285
|
-
* @param args - The tool call arguments (fallback source)
|
|
286
|
-
* @returns Array of todos if found, null otherwise
|
|
507
|
+
* Sends a session update to the ACP client.
|
|
508
|
+
* If a message rewriter is configured, updates pass through it first
|
|
509
|
+
* (original messages are sent as-is, rewritten versions are appended).
|
|
287
510
|
*/
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
if (args && Array.isArray(args["todos"])) {
|
|
292
|
-
return args["todos"];
|
|
511
|
+
async sendUpdate(update) {
|
|
512
|
+
if (this.ctx.messageRewriter) {
|
|
513
|
+
return this.ctx.messageRewriter.interceptUpdate(update);
|
|
293
514
|
}
|
|
294
|
-
return
|
|
515
|
+
return this.ctx.sendUpdate(update);
|
|
295
516
|
}
|
|
296
517
|
/**
|
|
297
|
-
*
|
|
298
|
-
* Handles both object and JSON string formats.
|
|
518
|
+
* Gets the session ID.
|
|
299
519
|
*/
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
if (typeof resultDisplay === "object") {
|
|
303
|
-
const obj = resultDisplay;
|
|
304
|
-
if (obj["type"] === "todo_list" && Array.isArray(obj["todos"])) {
|
|
305
|
-
return obj["todos"];
|
|
306
|
-
}
|
|
307
|
-
}
|
|
308
|
-
if (typeof resultDisplay === "string") {
|
|
309
|
-
try {
|
|
310
|
-
const parsed = JSON.parse(resultDisplay);
|
|
311
|
-
if (parsed?.["type"] === "todo_list" && Array.isArray(parsed["todos"])) {
|
|
312
|
-
return parsed["todos"];
|
|
313
|
-
}
|
|
314
|
-
} catch {
|
|
315
|
-
}
|
|
316
|
-
}
|
|
317
|
-
return null;
|
|
520
|
+
get sessionId() {
|
|
521
|
+
return this.ctx.sessionId;
|
|
318
522
|
}
|
|
319
523
|
};
|
|
320
524
|
|
|
321
|
-
// packages/cli/src/
|
|
322
|
-
|
|
323
|
-
function buildTruncatedDiffPreviewText(display) {
|
|
324
|
-
const fileName = typeof display["fileName"] === "string" ? display["fileName"] : "the edited file";
|
|
325
|
-
const fileDiffLength = typeof display["fileDiffLength"] === "number" ? ` Original fileDiff length: ${display["fileDiffLength"]} chars.` : "";
|
|
326
|
-
if (display["fileDiffTruncated"] === true) {
|
|
327
|
-
return `Full diff omitted from saved session history for ${fileName}.${fileDiffLength}`;
|
|
328
|
-
}
|
|
329
|
-
return `Saved session preview only for ${fileName}; full original and new file contents are unavailable.`;
|
|
330
|
-
}
|
|
331
|
-
__name(buildTruncatedDiffPreviewText, "buildTruncatedDiffPreviewText");
|
|
332
|
-
|
|
333
|
-
// packages/cli/src/acp-integration/session/emitters/ToolCallEmitter.ts
|
|
334
|
-
var KIND_MAP = {
|
|
335
|
-
["read" /* Read */]: "read",
|
|
336
|
-
["edit" /* Edit */]: "edit",
|
|
337
|
-
["delete" /* Delete */]: "delete",
|
|
338
|
-
["move" /* Move */]: "move",
|
|
339
|
-
["search" /* Search */]: "search",
|
|
340
|
-
["execute" /* Execute */]: "execute",
|
|
341
|
-
["think" /* Think */]: "think",
|
|
342
|
-
["fetch" /* Fetch */]: "fetch",
|
|
343
|
-
// ACP defines no 'agent' ToolKind (verified through @agentclientprotocol/sdk
|
|
344
|
-
// 0.25.1). The daemon's ClientSideConnection Zod-validates every session/update
|
|
345
|
-
// and session/request_permission from the `qwen --acp` child before fanning out
|
|
346
|
-
// to SSE clients, so emitting 'agent' is rejected at that hop and the frame is
|
|
347
|
-
// dropped. Map the internal Kind.Agent to 'other' on the wire to stay
|
|
348
|
-
// protocol-valid; dedicated agent UI is delivered out-of-band (via _meta.toolName)
|
|
349
|
-
// in a follow-up rather than via a kind the protocol can't carry.
|
|
350
|
-
["agent" /* Agent */]: "other",
|
|
351
|
-
["other" /* Other */]: "other"
|
|
352
|
-
};
|
|
353
|
-
var ToolCallEmitter = class _ToolCallEmitter extends BaseEmitter {
|
|
525
|
+
// packages/cli/src/acp-integration/session/emitters/MessageEmitter.ts
|
|
526
|
+
var MessageEmitter = class extends BaseEmitter {
|
|
354
527
|
static {
|
|
355
|
-
__name(this, "
|
|
356
|
-
}
|
|
357
|
-
planEmitter;
|
|
358
|
-
constructor(ctx) {
|
|
359
|
-
super(ctx);
|
|
360
|
-
this.planEmitter = new PlanEmitter(ctx);
|
|
528
|
+
__name(this, "MessageEmitter");
|
|
361
529
|
}
|
|
362
530
|
/**
|
|
363
|
-
* Emits a
|
|
531
|
+
* Emits a StopHookLoop event when Stop hooks create a loop.
|
|
532
|
+
* This informs the client that Stop hooks have been executed multiple times.
|
|
364
533
|
*
|
|
365
|
-
* @param
|
|
366
|
-
* @
|
|
534
|
+
* @param iterationCount - The current iteration count
|
|
535
|
+
* @param reasons - Array of reasons from each Stop hook execution
|
|
536
|
+
* @param stopHookCount - Number of Stop hooks that were executed
|
|
367
537
|
*/
|
|
368
|
-
async
|
|
369
|
-
|
|
370
|
-
return false;
|
|
371
|
-
}
|
|
372
|
-
const { title, locations, kind } = this.resolveToolMetadata(
|
|
373
|
-
params.toolName,
|
|
374
|
-
params.args
|
|
375
|
-
);
|
|
376
|
-
const provenance = _ToolCallEmitter.resolveToolProvenance(
|
|
377
|
-
params.toolName,
|
|
378
|
-
params.subagentMeta
|
|
379
|
-
);
|
|
538
|
+
async emitStopHookLoop(iterationCount, reasons, stopHookCount) {
|
|
539
|
+
const activeGoal = getActiveGoal(this.sessionId);
|
|
380
540
|
await this.sendUpdate({
|
|
381
|
-
sessionUpdate: "
|
|
382
|
-
|
|
383
|
-
status: params.status || "pending",
|
|
384
|
-
title,
|
|
385
|
-
content: [],
|
|
386
|
-
locations,
|
|
387
|
-
kind,
|
|
388
|
-
rawInput: params.args ?? {},
|
|
541
|
+
sessionUpdate: "agent_message_chunk",
|
|
542
|
+
content: { type: "text", text: "" },
|
|
389
543
|
_meta: {
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
544
|
+
stopHookLoop: {
|
|
545
|
+
iterationCount,
|
|
546
|
+
reasons,
|
|
547
|
+
stopHookCount,
|
|
548
|
+
...activeGoal ? {
|
|
549
|
+
goal: {
|
|
550
|
+
condition: activeGoal.condition,
|
|
551
|
+
iterations: activeGoal.iterations,
|
|
552
|
+
setAt: activeGoal.setAt,
|
|
553
|
+
lastReason: activeGoal.lastReason
|
|
554
|
+
}
|
|
555
|
+
} : {}
|
|
396
556
|
}
|
|
397
557
|
}
|
|
398
558
|
});
|
|
399
|
-
return true;
|
|
400
559
|
}
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
async emitResult(params) {
|
|
408
|
-
if (this.isTodoWriteTool(params.toolName)) {
|
|
409
|
-
const todos = this.planEmitter.extractTodos(
|
|
410
|
-
params.resultDisplay,
|
|
411
|
-
params.args
|
|
412
|
-
);
|
|
413
|
-
if (todos && todos.length > 0) {
|
|
414
|
-
await this.planEmitter.emitPlan(todos);
|
|
415
|
-
} else if (params.args && Array.isArray(params.args["todos"])) {
|
|
416
|
-
await this.planEmitter.emitPlan([]);
|
|
560
|
+
async emitGoalTerminal(event) {
|
|
561
|
+
await this.sendUpdate({
|
|
562
|
+
sessionUpdate: "agent_message_chunk",
|
|
563
|
+
content: { type: "text", text: "" },
|
|
564
|
+
_meta: {
|
|
565
|
+
goalTerminal: event
|
|
417
566
|
}
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
} else if (params.error) {
|
|
425
|
-
contentArray = [
|
|
426
|
-
{
|
|
427
|
-
type: "content",
|
|
428
|
-
content: { type: "text", text: params.error.message }
|
|
429
|
-
}
|
|
430
|
-
];
|
|
431
|
-
} else {
|
|
432
|
-
contentArray = this.transformPartsToToolCallContent(params.message);
|
|
433
|
-
}
|
|
434
|
-
const provenance = _ToolCallEmitter.resolveToolProvenance(
|
|
435
|
-
params.toolName,
|
|
436
|
-
params.subagentMeta
|
|
437
|
-
);
|
|
438
|
-
const update = {
|
|
439
|
-
sessionUpdate: "tool_call_update",
|
|
440
|
-
toolCallId: params.callId,
|
|
441
|
-
status: params.success ? "completed" : "failed",
|
|
442
|
-
content: contentArray,
|
|
567
|
+
});
|
|
568
|
+
}
|
|
569
|
+
async emitGoalStatus(status) {
|
|
570
|
+
await this.sendUpdate({
|
|
571
|
+
sessionUpdate: "agent_message_chunk",
|
|
572
|
+
content: { type: "text", text: "" },
|
|
443
573
|
_meta: {
|
|
444
|
-
|
|
445
|
-
...params.subagentMeta,
|
|
446
|
-
provenance: provenance.provenance,
|
|
447
|
-
...provenance.serverId ? { serverId: provenance.serverId } : {},
|
|
448
|
-
...BaseEmitter.toEpochMs(params.timestamp) != null && {
|
|
449
|
-
timestamp: BaseEmitter.toEpochMs(params.timestamp)
|
|
450
|
-
},
|
|
451
|
-
...params.artifacts && params.artifacts.length > 0 ? { artifacts: params.artifacts } : {}
|
|
574
|
+
goalStatus: status
|
|
452
575
|
}
|
|
453
|
-
};
|
|
454
|
-
if (params.resultDisplay !== void 0 && !this.isTruncatedSessionDiffDisplay(params.resultDisplay)) {
|
|
455
|
-
update["rawOutput"] = params.resultDisplay;
|
|
456
|
-
}
|
|
457
|
-
await this.sendUpdate(update);
|
|
576
|
+
});
|
|
458
577
|
}
|
|
459
578
|
/**
|
|
460
|
-
* Emits a
|
|
461
|
-
* Use this for explicit error handling when not using emitResult.
|
|
579
|
+
* Emits a user message chunk.
|
|
462
580
|
*
|
|
463
|
-
* @param
|
|
464
|
-
* @param
|
|
465
|
-
* @param error - The error that occurred
|
|
466
|
-
* @param subagentMeta - Optional subagent metadata
|
|
581
|
+
* @param text - The user message text content
|
|
582
|
+
* @param timestamp - Optional server-side timestamp (ISO string or ms) for message ordering
|
|
467
583
|
*/
|
|
468
|
-
async
|
|
469
|
-
const
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
584
|
+
async emitUserMessage(text, timestamp, options = {}) {
|
|
585
|
+
const epochMs = BaseEmitter.toEpochMs(timestamp);
|
|
586
|
+
const _meta = {
|
|
587
|
+
...epochMs != null ? { timestamp: epochMs } : {},
|
|
588
|
+
...options.source ? { source: options.source } : {}
|
|
589
|
+
};
|
|
473
590
|
await this.sendUpdate({
|
|
474
|
-
sessionUpdate: "
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
content: [
|
|
478
|
-
{ type: "content", content: { type: "text", text: error.message } }
|
|
479
|
-
],
|
|
480
|
-
_meta: {
|
|
481
|
-
toolName,
|
|
482
|
-
...subagentMeta,
|
|
483
|
-
provenance: provenance.provenance,
|
|
484
|
-
...provenance.serverId ? { serverId: provenance.serverId } : {}
|
|
485
|
-
}
|
|
591
|
+
sessionUpdate: "user_message_chunk",
|
|
592
|
+
content: { type: "text", text },
|
|
593
|
+
...Object.keys(_meta).length > 0 ? { _meta } : {}
|
|
486
594
|
});
|
|
487
595
|
}
|
|
488
596
|
/**
|
|
489
|
-
*
|
|
490
|
-
* The SDK reads `_meta.
|
|
491
|
-
* provenance` + `_meta.serverId` to render builtin / MCP-server-badge /
|
|
492
|
-
* subagent-block differently. Without this stamping, the SDK falls
|
|
493
|
-
* back to string-matching the toolName which can't reliably
|
|
494
|
-
* distinguish builtin from subagent.
|
|
495
|
-
*
|
|
496
|
-
* Resolution rules:
|
|
497
|
-
* - `subagentMeta` present → `'subagent'` (a Task tool / Codex
|
|
498
|
-
* subagent / etc. wrapping its own tool calls)
|
|
499
|
-
* - toolName matches `mcp__<server>__<tool>` → `'mcp'` with
|
|
500
|
-
* `serverId: <server>`. Naming convention from
|
|
501
|
-
* `packages/core/src/tools/mcp-tool.ts` in the
|
|
502
|
-
* `@qwen-code/qwen-code-core` package — mirrors the SDK's same
|
|
503
|
-
* heuristic fallback so SDK consumers stay consistent with
|
|
504
|
-
* daemon classification.
|
|
505
|
-
* - everything else → `'builtin'`
|
|
597
|
+
* Emits an agent thought chunk.
|
|
506
598
|
*
|
|
507
|
-
*
|
|
508
|
-
*
|
|
599
|
+
* @param text - The thought text content
|
|
600
|
+
* @param timestamp - Optional server-side timestamp (ISO string or ms) for message ordering
|
|
509
601
|
*/
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
}
|
|
519
|
-
}
|
|
520
|
-
return { provenance: "builtin" };
|
|
602
|
+
async emitAgentThought(text, timestamp, subagentMeta) {
|
|
603
|
+
const _meta = this.buildChunkMeta(
|
|
604
|
+
BaseEmitter.toEpochMs(timestamp),
|
|
605
|
+
subagentMeta
|
|
606
|
+
);
|
|
607
|
+
await this.sendUpdate({
|
|
608
|
+
sessionUpdate: "agent_thought_chunk",
|
|
609
|
+
content: { type: "text", text },
|
|
610
|
+
..._meta ? { _meta } : {}
|
|
611
|
+
});
|
|
521
612
|
}
|
|
522
|
-
// ==================== Public Utilities ====================
|
|
523
613
|
/**
|
|
524
|
-
*
|
|
525
|
-
*
|
|
614
|
+
* Emits an agent message chunk.
|
|
615
|
+
*
|
|
616
|
+
* @param text - The agent message text content
|
|
617
|
+
* @param timestamp - Optional server-side timestamp (ISO string or ms) for message ordering
|
|
526
618
|
*/
|
|
527
|
-
|
|
528
|
-
|
|
619
|
+
async emitAgentMessage(text, timestamp, subagentMeta) {
|
|
620
|
+
const _meta = this.buildChunkMeta(
|
|
621
|
+
BaseEmitter.toEpochMs(timestamp),
|
|
622
|
+
subagentMeta
|
|
623
|
+
);
|
|
624
|
+
await this.sendUpdate({
|
|
625
|
+
sessionUpdate: "agent_message_chunk",
|
|
626
|
+
content: { type: "text", text },
|
|
627
|
+
..._meta ? { _meta } : {}
|
|
628
|
+
});
|
|
529
629
|
}
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
630
|
+
async emitSlashCommandOutput(text, timestamp) {
|
|
631
|
+
const epochMs = BaseEmitter.toEpochMs(timestamp);
|
|
632
|
+
await this.sendUpdate({
|
|
633
|
+
sessionUpdate: "agent_message_chunk",
|
|
634
|
+
content: { type: "text", text },
|
|
635
|
+
_meta: {
|
|
636
|
+
source: "slash_command",
|
|
637
|
+
...epochMs != null ? { timestamp: epochMs } : {}
|
|
638
|
+
}
|
|
639
|
+
});
|
|
535
640
|
}
|
|
536
641
|
/**
|
|
537
|
-
*
|
|
642
|
+
* Emits usage metadata.
|
|
538
643
|
*/
|
|
539
|
-
|
|
540
|
-
|
|
644
|
+
async emitUsageMetadata(usageMetadata, text = "", durationMs, subagentMeta) {
|
|
645
|
+
const usage = {
|
|
646
|
+
inputTokens: usageMetadata.promptTokenCount ?? 0,
|
|
647
|
+
outputTokens: usageMetadata.candidatesTokenCount ?? 0,
|
|
648
|
+
totalTokens: usageMetadata.totalTokenCount ?? 0,
|
|
649
|
+
thoughtTokens: usageMetadata.thoughtsTokenCount,
|
|
650
|
+
cachedReadTokens: usageMetadata.cachedContentTokenCount
|
|
651
|
+
};
|
|
652
|
+
const cumulative = this.ctx.cumulativeUsage;
|
|
653
|
+
if (cumulative) {
|
|
654
|
+
const addFinite = /* @__PURE__ */ __name((total, value) => typeof value === "number" && Number.isFinite(value) ? total + value : total, "addFinite");
|
|
655
|
+
cumulative.promptTokens = addFinite(
|
|
656
|
+
cumulative.promptTokens,
|
|
657
|
+
usage.inputTokens
|
|
658
|
+
);
|
|
659
|
+
cumulative.candidateTokens = addFinite(
|
|
660
|
+
cumulative.candidateTokens,
|
|
661
|
+
usage.outputTokens
|
|
662
|
+
);
|
|
663
|
+
cumulative.cachedTokens = addFinite(
|
|
664
|
+
cumulative.cachedTokens,
|
|
665
|
+
usage.cachedReadTokens
|
|
666
|
+
);
|
|
667
|
+
cumulative.apiTimeMs = addFinite(cumulative.apiTimeMs, durationMs);
|
|
668
|
+
}
|
|
669
|
+
let meta;
|
|
670
|
+
if (typeof durationMs === "number") {
|
|
671
|
+
const activity = apiActivityTracker.drain();
|
|
672
|
+
meta = {
|
|
673
|
+
usage,
|
|
674
|
+
durationMs,
|
|
675
|
+
...activity.errors > 0 ? { apiErrors: activity.errors } : {},
|
|
676
|
+
...activity.retries > 0 ? { apiRetries: activity.retries } : {},
|
|
677
|
+
...subagentMeta
|
|
678
|
+
};
|
|
679
|
+
} else {
|
|
680
|
+
meta = { usage, ...subagentMeta };
|
|
681
|
+
}
|
|
682
|
+
await this.sendUpdate({
|
|
683
|
+
sessionUpdate: "agent_message_chunk",
|
|
684
|
+
content: { type: "text", text },
|
|
685
|
+
_meta: meta
|
|
686
|
+
});
|
|
541
687
|
}
|
|
542
688
|
/**
|
|
543
|
-
*
|
|
544
|
-
*
|
|
689
|
+
* Emits a message chunk based on role and thought flag.
|
|
690
|
+
* This is the unified method that handles all message types.
|
|
545
691
|
*
|
|
546
|
-
* @param
|
|
547
|
-
* @param
|
|
692
|
+
* @param text - The message text content
|
|
693
|
+
* @param role - Whether this is a user or assistant message
|
|
694
|
+
* @param isThought - Whether this is an assistant thought (only applies to assistant role)
|
|
695
|
+
* @param timestamp - Optional server-side timestamp (ISO string or ms) for message ordering
|
|
548
696
|
*/
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
let title = tool?.displayName ?? toolName;
|
|
553
|
-
let locations = [];
|
|
554
|
-
let kind = "other";
|
|
555
|
-
if (tool && args) {
|
|
556
|
-
try {
|
|
557
|
-
const invocation = tool.build(args);
|
|
558
|
-
title = `${title}: ${invocation.getDescription()}`;
|
|
559
|
-
locations = invocation.toolLocations().map((loc) => ({
|
|
560
|
-
path: loc.path,
|
|
561
|
-
line: loc.line ?? null
|
|
562
|
-
}));
|
|
563
|
-
kind = this.mapToolKind(tool.kind, toolName);
|
|
564
|
-
} catch {
|
|
565
|
-
if (typeof args["description"] === "string") {
|
|
566
|
-
title = `${title}: ${args["description"]}`;
|
|
567
|
-
}
|
|
568
|
-
if (tool.kind) {
|
|
569
|
-
kind = this.mapToolKind(tool.kind, toolName);
|
|
570
|
-
}
|
|
571
|
-
}
|
|
697
|
+
async emitMessage(text, role, isThought = false, timestamp, subagentMeta) {
|
|
698
|
+
if (role === "user") {
|
|
699
|
+
return this.emitUserMessage(text, timestamp);
|
|
572
700
|
}
|
|
573
|
-
return
|
|
701
|
+
return isThought ? this.emitAgentThought(text, timestamp, subagentMeta) : this.emitAgentMessage(text, timestamp, subagentMeta);
|
|
702
|
+
}
|
|
703
|
+
buildChunkMeta(epochMs, subagentMeta) {
|
|
704
|
+
const meta = {
|
|
705
|
+
...subagentMeta?.parentToolCallId ? { parentToolCallId: subagentMeta.parentToolCallId } : {},
|
|
706
|
+
...subagentMeta?.subagentType ? { subagentType: subagentMeta.subagentType } : {},
|
|
707
|
+
...epochMs != null ? { timestamp: epochMs } : {}
|
|
708
|
+
};
|
|
709
|
+
return Object.keys(meta).length > 0 ? meta : void 0;
|
|
710
|
+
}
|
|
711
|
+
};
|
|
712
|
+
|
|
713
|
+
// packages/cli/src/acp-integration/session/emitters/PlanEmitter.ts
|
|
714
|
+
init_esbuild_shims();
|
|
715
|
+
var PlanEmitter = class extends BaseEmitter {
|
|
716
|
+
static {
|
|
717
|
+
__name(this, "PlanEmitter");
|
|
574
718
|
}
|
|
575
719
|
/**
|
|
576
|
-
*
|
|
720
|
+
* Emits a plan update with the given todo items.
|
|
577
721
|
*
|
|
578
|
-
* @param
|
|
579
|
-
* @param toolName - Optional tool name to handle special cases like exit_plan_mode
|
|
722
|
+
* @param todos - Array of todo items to send as plan entries
|
|
580
723
|
*/
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
724
|
+
async emitPlan(todos) {
|
|
725
|
+
const entries = todos.map((todo) => ({
|
|
726
|
+
content: todo.content,
|
|
727
|
+
priority: "medium",
|
|
728
|
+
// Default priority since todos don't have priority
|
|
729
|
+
status: todo.status
|
|
730
|
+
}));
|
|
731
|
+
const cumulative = this.ctx.cumulativeUsage;
|
|
732
|
+
await this.sendUpdate({
|
|
733
|
+
sessionUpdate: "plan",
|
|
734
|
+
entries,
|
|
735
|
+
...cumulative ? { _meta: { stats: { ...cumulative } } } : {}
|
|
736
|
+
});
|
|
586
737
|
}
|
|
587
|
-
// ==================== Private Helpers ====================
|
|
588
738
|
/**
|
|
589
|
-
* Extracts
|
|
590
|
-
*
|
|
739
|
+
* Extracts todos from tool result display or args.
|
|
740
|
+
* Tries multiple sources in priority order:
|
|
741
|
+
* 1. Result display object with type 'todo_list'
|
|
742
|
+
* 2. Result display as JSON string
|
|
743
|
+
* 3. Args with 'todos' array
|
|
744
|
+
*
|
|
745
|
+
* @param resultDisplay - The tool result display (object, string, or undefined)
|
|
746
|
+
* @param args - The tool call arguments (fallback source)
|
|
747
|
+
* @returns Array of todos if found, null otherwise
|
|
591
748
|
*/
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
if (
|
|
596
|
-
|
|
597
|
-
return {
|
|
598
|
-
type: "content",
|
|
599
|
-
content: {
|
|
600
|
-
type: "text",
|
|
601
|
-
text: buildTruncatedDiffPreviewText(obj)
|
|
602
|
-
}
|
|
603
|
-
};
|
|
604
|
-
}
|
|
605
|
-
return {
|
|
606
|
-
type: "diff",
|
|
607
|
-
path: obj["fileName"],
|
|
608
|
-
oldText: obj["originalContent"] ?? "",
|
|
609
|
-
newText: obj["newContent"]
|
|
610
|
-
};
|
|
749
|
+
extractTodos(resultDisplay, args) {
|
|
750
|
+
const fromDisplay = this.extractFromResultDisplay(resultDisplay);
|
|
751
|
+
if (fromDisplay) return fromDisplay;
|
|
752
|
+
if (args && Array.isArray(args["todos"])) {
|
|
753
|
+
return args["todos"];
|
|
611
754
|
}
|
|
612
755
|
return null;
|
|
613
756
|
}
|
|
614
|
-
isTruncatedSessionDiffDisplay(resultDisplay) {
|
|
615
|
-
if (!resultDisplay || typeof resultDisplay !== "object") return false;
|
|
616
|
-
const obj = resultDisplay;
|
|
617
|
-
return obj["truncatedForSession"] === true && "fileName" in obj && "newContent" in obj;
|
|
618
|
-
}
|
|
619
757
|
/**
|
|
620
|
-
*
|
|
621
|
-
*
|
|
758
|
+
* Extracts todos from a result display value.
|
|
759
|
+
* Handles both object and JSON string formats.
|
|
622
760
|
*/
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
content: { type: "text", text: part.text }
|
|
630
|
-
});
|
|
761
|
+
extractFromResultDisplay(resultDisplay) {
|
|
762
|
+
if (!resultDisplay) return null;
|
|
763
|
+
if (typeof resultDisplay === "object") {
|
|
764
|
+
const obj = resultDisplay;
|
|
765
|
+
if (obj["type"] === "todo_list" && Array.isArray(obj["todos"])) {
|
|
766
|
+
return obj["todos"];
|
|
631
767
|
}
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
result.push({
|
|
639
|
-
type: "content",
|
|
640
|
-
content: { type: "text", text: responseText }
|
|
641
|
-
});
|
|
642
|
-
} catch {
|
|
768
|
+
}
|
|
769
|
+
if (typeof resultDisplay === "string") {
|
|
770
|
+
try {
|
|
771
|
+
const parsed = JSON.parse(resultDisplay);
|
|
772
|
+
if (parsed?.["type"] === "todo_list" && Array.isArray(parsed["todos"])) {
|
|
773
|
+
return parsed["todos"];
|
|
643
774
|
}
|
|
775
|
+
} catch {
|
|
644
776
|
}
|
|
645
777
|
}
|
|
646
|
-
return
|
|
778
|
+
return null;
|
|
647
779
|
}
|
|
648
780
|
};
|
|
649
781
|
|
|
650
|
-
// packages/cli/src/
|
|
782
|
+
// packages/cli/src/acp-integration/session/emitters/tool-call-emitter.ts
|
|
651
783
|
init_esbuild_shims();
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
const resultCallId = record.toolCallResult?.callId;
|
|
658
|
-
if (typeof resultCallId === "string" && resultCallId.length > 0) {
|
|
659
|
-
return resultCallId;
|
|
660
|
-
}
|
|
661
|
-
return extractFunctionResponseId(record);
|
|
784
|
+
|
|
785
|
+
// packages/cli/src/acp-integration/session/types.ts
|
|
786
|
+
init_esbuild_shims();
|
|
787
|
+
function hasFullSessionContext(context) {
|
|
788
|
+
return "config" in context;
|
|
662
789
|
}
|
|
663
|
-
__name(
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
}
|
|
790
|
+
__name(hasFullSessionContext, "hasFullSessionContext");
|
|
791
|
+
|
|
792
|
+
// packages/cli/src/utils/truncatedDiffPreview.ts
|
|
793
|
+
init_esbuild_shims();
|
|
794
|
+
function buildTruncatedDiffPreviewText(display) {
|
|
795
|
+
const fileName = typeof display["fileName"] === "string" ? display["fileName"] : "the edited file";
|
|
796
|
+
const fileDiffLength = typeof display["fileDiffLength"] === "number" ? ` Original fileDiff length: ${display["fileDiffLength"]} chars.` : "";
|
|
797
|
+
if (display["fileDiffTruncated"] === true) {
|
|
798
|
+
return `Full diff omitted from saved session history for ${fileName}.${fileDiffLength}`;
|
|
673
799
|
}
|
|
674
|
-
return
|
|
800
|
+
return `Saved session preview only for ${fileName}; full original and new file contents are unavailable.`;
|
|
675
801
|
}
|
|
676
|
-
__name(
|
|
802
|
+
__name(buildTruncatedDiffPreviewText, "buildTruncatedDiffPreviewText");
|
|
677
803
|
|
|
678
|
-
// packages/cli/src/acp-integration/session/
|
|
679
|
-
var
|
|
680
|
-
|
|
804
|
+
// packages/cli/src/acp-integration/session/emitters/tool-call-emitter.ts
|
|
805
|
+
var KIND_MAP = {
|
|
806
|
+
["read" /* Read */]: "read",
|
|
807
|
+
["edit" /* Edit */]: "edit",
|
|
808
|
+
["delete" /* Delete */]: "delete",
|
|
809
|
+
["move" /* Move */]: "move",
|
|
810
|
+
["search" /* Search */]: "search",
|
|
811
|
+
["execute" /* Execute */]: "execute",
|
|
812
|
+
["think" /* Think */]: "think",
|
|
813
|
+
["fetch" /* Fetch */]: "fetch",
|
|
814
|
+
// ACP defines no 'agent' ToolKind (verified through @agentclientprotocol/sdk
|
|
815
|
+
// 0.25.1). The daemon's ClientSideConnection Zod-validates every session/update
|
|
816
|
+
// and session/request_permission from the `qwen --acp` child before fanning out
|
|
817
|
+
// to SSE clients, so emitting 'agent' is rejected at that hop and the frame is
|
|
818
|
+
// dropped. Map the internal Kind.Agent to 'other' on the wire to stay
|
|
819
|
+
// protocol-valid; dedicated agent UI is delivered out-of-band (via _meta.toolName)
|
|
820
|
+
// in a follow-up rather than via a kind the protocol can't carry.
|
|
821
|
+
["agent" /* Agent */]: "other",
|
|
822
|
+
["other" /* Other */]: "other"
|
|
823
|
+
};
|
|
824
|
+
var ToolCallEmitter = class _ToolCallEmitter extends BaseEmitter {
|
|
681
825
|
static {
|
|
682
|
-
__name(this, "
|
|
826
|
+
__name(this, "ToolCallEmitter");
|
|
683
827
|
}
|
|
684
|
-
|
|
685
|
-
messageEmitter;
|
|
686
|
-
toolCallEmitter;
|
|
687
|
-
pendingReplayToolCalls = /* @__PURE__ */ new Map();
|
|
828
|
+
planEmitter;
|
|
688
829
|
constructor(ctx) {
|
|
689
|
-
|
|
690
|
-
this.
|
|
691
|
-
this.toolCallEmitter = new ToolCallEmitter(ctx);
|
|
830
|
+
super(ctx);
|
|
831
|
+
this.planEmitter = new PlanEmitter(ctx);
|
|
692
832
|
}
|
|
693
833
|
/**
|
|
694
|
-
*
|
|
834
|
+
* Emits a tool call start event.
|
|
695
835
|
*
|
|
696
|
-
* @param
|
|
697
|
-
* @
|
|
698
|
-
* immediately before each gap's child record so the user sees that an
|
|
699
|
-
* earlier segment was lost rather than assuming the halves are contiguous.
|
|
836
|
+
* @param params - Tool call start parameters
|
|
837
|
+
* @returns true if event was emitted, false if skipped (e.g., TodoWriteTool)
|
|
700
838
|
*/
|
|
701
|
-
async
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
} finally {
|
|
705
|
-
this.pendingReplayToolCalls.clear();
|
|
706
|
-
this.setActiveRecordId(null);
|
|
707
|
-
}
|
|
708
|
-
}
|
|
709
|
-
async replayPage(records, options = {}) {
|
|
710
|
-
this.pendingReplayToolCalls.clear();
|
|
711
|
-
for (const pending of options.pendingToolCalls ?? []) {
|
|
712
|
-
this.pendingReplayToolCalls.set(pending.callId, pending);
|
|
839
|
+
async emitStart(params) {
|
|
840
|
+
if (this.isTodoWriteTool(params.toolName)) {
|
|
841
|
+
return false;
|
|
713
842
|
}
|
|
714
|
-
const
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
843
|
+
const { title, locations, kind } = this.resolveToolMetadata(
|
|
844
|
+
params.toolName,
|
|
845
|
+
params.args
|
|
846
|
+
);
|
|
847
|
+
const provenance = _ToolCallEmitter.resolveToolProvenance(
|
|
848
|
+
params.toolName,
|
|
849
|
+
params.subagentMeta
|
|
850
|
+
);
|
|
851
|
+
await this.sendUpdate({
|
|
852
|
+
sessionUpdate: "tool_call",
|
|
853
|
+
toolCallId: params.callId,
|
|
854
|
+
status: params.status || "pending",
|
|
855
|
+
title,
|
|
856
|
+
content: [],
|
|
857
|
+
locations,
|
|
858
|
+
kind,
|
|
859
|
+
rawInput: params.args ?? {},
|
|
860
|
+
_meta: {
|
|
861
|
+
toolName: params.toolName,
|
|
862
|
+
...params.subagentMeta,
|
|
863
|
+
provenance: provenance.provenance,
|
|
864
|
+
...provenance.serverId ? { serverId: provenance.serverId } : {},
|
|
865
|
+
...BaseEmitter.toEpochMs(params.timestamp) != null && {
|
|
866
|
+
timestamp: BaseEmitter.toEpochMs(params.timestamp)
|
|
721
867
|
}
|
|
722
|
-
await this.replayRecord(record);
|
|
723
868
|
}
|
|
724
|
-
}
|
|
725
|
-
|
|
726
|
-
}
|
|
727
|
-
let danglingError;
|
|
728
|
-
if (options.finalizeDangling === true) {
|
|
729
|
-
try {
|
|
730
|
-
await this.failDanglingToolCalls();
|
|
731
|
-
} catch (error) {
|
|
732
|
-
danglingError = error;
|
|
733
|
-
}
|
|
734
|
-
}
|
|
735
|
-
const state = {
|
|
736
|
-
pendingToolCalls: options.finalizeDangling === true ? [] : Array.from(this.pendingReplayToolCalls.values())
|
|
737
|
-
};
|
|
738
|
-
this.setActiveRecordId(null);
|
|
739
|
-
if (replayError && danglingError) {
|
|
740
|
-
throw new AggregateError(
|
|
741
|
-
[replayError, danglingError],
|
|
742
|
-
"Replay and dangling-cleanup both failed"
|
|
743
|
-
);
|
|
744
|
-
}
|
|
745
|
-
if (replayError) {
|
|
746
|
-
throw replayError;
|
|
747
|
-
}
|
|
748
|
-
if (danglingError) {
|
|
749
|
-
throw danglingError;
|
|
750
|
-
}
|
|
751
|
-
return state;
|
|
752
|
-
}
|
|
753
|
-
getPendingToolCalls() {
|
|
754
|
-
return Array.from(this.pendingReplayToolCalls.values());
|
|
869
|
+
});
|
|
870
|
+
return true;
|
|
755
871
|
}
|
|
756
872
|
/**
|
|
757
|
-
*
|
|
873
|
+
* Emits a tool call result event.
|
|
874
|
+
* Handles TodoWriteTool specially by routing to plan updates.
|
|
875
|
+
*
|
|
876
|
+
* @param params - Tool call result parameters
|
|
758
877
|
*/
|
|
759
|
-
async
|
|
760
|
-
this.
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
record.timestamp,
|
|
770
|
-
record.subtype === "cron" ? { source: "cron" } : void 0
|
|
771
|
-
);
|
|
772
|
-
}
|
|
773
|
-
break;
|
|
774
|
-
}
|
|
775
|
-
if (record.subtype === "mid_turn_user_message") {
|
|
776
|
-
const displayText = record.systemPayload?.displayText;
|
|
777
|
-
if (displayText) {
|
|
778
|
-
await this.messageEmitter.emitUserMessage(
|
|
779
|
-
displayText,
|
|
780
|
-
record.timestamp
|
|
781
|
-
);
|
|
782
|
-
} else if (record.message) {
|
|
783
|
-
await this.replayContent(
|
|
784
|
-
record.message,
|
|
785
|
-
"user",
|
|
786
|
-
record.timestamp,
|
|
787
|
-
record.uuid
|
|
788
|
-
);
|
|
789
|
-
}
|
|
790
|
-
break;
|
|
791
|
-
}
|
|
792
|
-
if (record.message) {
|
|
793
|
-
await this.replayContent(
|
|
794
|
-
record.message,
|
|
795
|
-
"user",
|
|
796
|
-
record.timestamp,
|
|
797
|
-
record.uuid
|
|
798
|
-
);
|
|
799
|
-
}
|
|
800
|
-
break;
|
|
801
|
-
case "assistant":
|
|
802
|
-
if (record.message) {
|
|
803
|
-
await this.replayContent(
|
|
804
|
-
record.message,
|
|
805
|
-
"assistant",
|
|
806
|
-
record.timestamp,
|
|
807
|
-
record.uuid
|
|
808
|
-
);
|
|
809
|
-
}
|
|
810
|
-
if (record.usageMetadata) {
|
|
811
|
-
await this.replayUsageMetadata(record.usageMetadata);
|
|
812
|
-
}
|
|
813
|
-
break;
|
|
814
|
-
case "tool_result":
|
|
815
|
-
await this.replayToolResult(record);
|
|
816
|
-
break;
|
|
817
|
-
case "system":
|
|
818
|
-
if (record.subtype === "slash_command") {
|
|
819
|
-
await this.replaySlashCommandResult(record);
|
|
820
|
-
}
|
|
821
|
-
break;
|
|
822
|
-
default:
|
|
823
|
-
break;
|
|
878
|
+
async emitResult(params) {
|
|
879
|
+
if (this.isTodoWriteTool(params.toolName)) {
|
|
880
|
+
const todos = this.planEmitter.extractTodos(
|
|
881
|
+
params.resultDisplay,
|
|
882
|
+
params.args
|
|
883
|
+
);
|
|
884
|
+
if (todos && todos.length > 0) {
|
|
885
|
+
await this.planEmitter.emitPlan(todos);
|
|
886
|
+
} else if (params.args && Array.isArray(params.args["todos"])) {
|
|
887
|
+
await this.planEmitter.emitPlan([]);
|
|
824
888
|
}
|
|
825
|
-
|
|
826
|
-
|
|
889
|
+
return;
|
|
890
|
+
}
|
|
891
|
+
let contentArray = [];
|
|
892
|
+
const diffContent = this.extractDiffContent(params.resultDisplay);
|
|
893
|
+
if (diffContent) {
|
|
894
|
+
contentArray = [diffContent];
|
|
895
|
+
} else if (params.error) {
|
|
896
|
+
contentArray = [
|
|
897
|
+
{
|
|
898
|
+
type: "content",
|
|
899
|
+
content: { type: "text", text: params.error.message }
|
|
900
|
+
}
|
|
901
|
+
];
|
|
902
|
+
} else {
|
|
903
|
+
contentArray = this.transformPartsToToolCallContent(params.message);
|
|
904
|
+
}
|
|
905
|
+
const provenance = _ToolCallEmitter.resolveToolProvenance(
|
|
906
|
+
params.toolName,
|
|
907
|
+
params.subagentMeta
|
|
908
|
+
);
|
|
909
|
+
const update = {
|
|
910
|
+
sessionUpdate: "tool_call_update",
|
|
911
|
+
toolCallId: params.callId,
|
|
912
|
+
status: params.success ? "completed" : "failed",
|
|
913
|
+
content: contentArray,
|
|
914
|
+
_meta: {
|
|
915
|
+
toolName: params.toolName,
|
|
916
|
+
...params.subagentMeta,
|
|
917
|
+
provenance: provenance.provenance,
|
|
918
|
+
...provenance.serverId ? { serverId: provenance.serverId } : {},
|
|
919
|
+
...BaseEmitter.toEpochMs(params.timestamp) != null && {
|
|
920
|
+
timestamp: BaseEmitter.toEpochMs(params.timestamp)
|
|
921
|
+
},
|
|
922
|
+
...params.artifacts && params.artifacts.length > 0 ? { artifacts: params.artifacts } : {}
|
|
923
|
+
}
|
|
924
|
+
};
|
|
925
|
+
if (params.resultDisplay !== void 0 && !this.isTruncatedSessionDiffDisplay(params.resultDisplay)) {
|
|
926
|
+
update["rawOutput"] = params.resultDisplay;
|
|
827
927
|
}
|
|
928
|
+
await this.sendUpdate(update);
|
|
828
929
|
}
|
|
829
930
|
/**
|
|
830
|
-
* Emits a
|
|
831
|
-
*
|
|
832
|
-
*
|
|
833
|
-
*
|
|
834
|
-
*
|
|
835
|
-
*
|
|
931
|
+
* Emits a tool call error event.
|
|
932
|
+
* Use this for explicit error handling when not using emitResult.
|
|
933
|
+
*
|
|
934
|
+
* @param callId - The tool call ID
|
|
935
|
+
* @param toolName - The tool name
|
|
936
|
+
* @param error - The error that occurred
|
|
937
|
+
* @param subagentMeta - Optional subagent metadata
|
|
836
938
|
*/
|
|
837
|
-
async
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
939
|
+
async emitError(callId, toolName, error, subagentMeta) {
|
|
940
|
+
const provenance = _ToolCallEmitter.resolveToolProvenance(
|
|
941
|
+
toolName,
|
|
942
|
+
subagentMeta
|
|
841
943
|
);
|
|
944
|
+
await this.sendUpdate({
|
|
945
|
+
sessionUpdate: "tool_call_update",
|
|
946
|
+
toolCallId: callId,
|
|
947
|
+
status: "failed",
|
|
948
|
+
content: [
|
|
949
|
+
{ type: "content", content: { type: "text", text: error.message } }
|
|
950
|
+
],
|
|
951
|
+
_meta: {
|
|
952
|
+
toolName,
|
|
953
|
+
...subagentMeta,
|
|
954
|
+
provenance: provenance.provenance,
|
|
955
|
+
...provenance.serverId ? { serverId: provenance.serverId } : {}
|
|
956
|
+
}
|
|
957
|
+
});
|
|
842
958
|
}
|
|
843
959
|
/**
|
|
844
|
-
*
|
|
845
|
-
*
|
|
960
|
+
* Resolve a tool's provenance for UI dispatch on tool_call events.
|
|
961
|
+
* The SDK reads `_meta.
|
|
962
|
+
* provenance` + `_meta.serverId` to render builtin / MCP-server-badge /
|
|
963
|
+
* subagent-block differently. Without this stamping, the SDK falls
|
|
964
|
+
* back to string-matching the toolName which can't reliably
|
|
965
|
+
* distinguish builtin from subagent.
|
|
846
966
|
*
|
|
847
|
-
*
|
|
848
|
-
*
|
|
849
|
-
*
|
|
967
|
+
* Resolution rules:
|
|
968
|
+
* - `subagentMeta` present → `'subagent'` (a Task tool / Codex
|
|
969
|
+
* subagent / etc. wrapping its own tool calls)
|
|
970
|
+
* - toolName matches `mcp__<server>__<tool>` → `'mcp'` with
|
|
971
|
+
* `serverId: <server>`. Naming convention from
|
|
972
|
+
* `packages/core/src/tools/mcp-tool.ts` in the
|
|
973
|
+
* `@qwen-code/qwen-code-core` package — mirrors the SDK's same
|
|
974
|
+
* heuristic fallback so SDK consumers stay consistent with
|
|
975
|
+
* daemon classification.
|
|
976
|
+
* - everything else → `'builtin'`
|
|
977
|
+
*
|
|
978
|
+
* Static + pure so it can be unit-tested without an emitter
|
|
979
|
+
* instance. Exported via `ToolCallEmitter.resolveToolProvenance`.
|
|
850
980
|
*/
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
timestamp
|
|
860
|
-
);
|
|
861
|
-
}
|
|
862
|
-
if ("functionCall" in part && part.functionCall) {
|
|
863
|
-
const functionName = part.functionCall.name ?? "";
|
|
864
|
-
const sourceCallId = part.functionCall.id;
|
|
865
|
-
const callId = sourceCallId ?? `${functionName}-${Date.now()}`;
|
|
866
|
-
const emitted = await this.toolCallEmitter.emitStart({
|
|
867
|
-
toolName: functionName,
|
|
868
|
-
callId,
|
|
869
|
-
args: part.functionCall.args,
|
|
870
|
-
status: "in_progress",
|
|
871
|
-
timestamp
|
|
872
|
-
});
|
|
873
|
-
if (emitted && role === "assistant" && recordId && sourceCallId) {
|
|
874
|
-
this.pendingReplayToolCalls.set(callId, {
|
|
875
|
-
callId,
|
|
876
|
-
toolName: functionName,
|
|
877
|
-
timestamp,
|
|
878
|
-
recordId
|
|
879
|
-
});
|
|
880
|
-
}
|
|
981
|
+
static resolveToolProvenance(toolName, subagentMeta) {
|
|
982
|
+
if (subagentMeta !== void 0) {
|
|
983
|
+
return { provenance: "subagent" };
|
|
984
|
+
}
|
|
985
|
+
if (toolName.startsWith("mcp__")) {
|
|
986
|
+
const parts = toolName.split("__");
|
|
987
|
+
if (parts.length >= 3 && parts[1] && parts[1].length > 0) {
|
|
988
|
+
return { provenance: "mcp", serverId: parts[1] };
|
|
881
989
|
}
|
|
882
990
|
}
|
|
991
|
+
return { provenance: "builtin" };
|
|
883
992
|
}
|
|
993
|
+
// ==================== Public Utilities ====================
|
|
884
994
|
/**
|
|
885
|
-
*
|
|
886
|
-
*
|
|
995
|
+
* Checks if a tool name is the TodoWriteTool.
|
|
996
|
+
* Exposed for external use in components that need to check this.
|
|
887
997
|
*/
|
|
888
|
-
|
|
889
|
-
|
|
998
|
+
isTodoWriteTool(toolName) {
|
|
999
|
+
return toolName === ToolNames.TODO_WRITE;
|
|
890
1000
|
}
|
|
891
1001
|
/**
|
|
892
|
-
*
|
|
1002
|
+
* Checks if a tool name is the ExitPlanModeTool.
|
|
893
1003
|
*/
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
return;
|
|
897
|
-
}
|
|
898
|
-
const result = record.toolCallResult;
|
|
899
|
-
const callId = getToolResultCallId(record);
|
|
900
|
-
this.pendingReplayToolCalls.delete(callId);
|
|
901
|
-
const toolName = this.extractToolNameFromRecord(record);
|
|
902
|
-
await this.toolCallEmitter.emitResult({
|
|
903
|
-
toolName,
|
|
904
|
-
callId,
|
|
905
|
-
success: !result?.error,
|
|
906
|
-
message: record.message.parts,
|
|
907
|
-
resultDisplay: result?.resultDisplay,
|
|
908
|
-
artifacts: result?.artifacts,
|
|
909
|
-
// For TodoWriteTool fallback, try to extract args from the record
|
|
910
|
-
// Note: args aren't stored in tool_result records by default
|
|
911
|
-
args: void 0,
|
|
912
|
-
timestamp: record.timestamp
|
|
913
|
-
});
|
|
914
|
-
const { resultDisplay } = result ?? {};
|
|
915
|
-
if (!!resultDisplay && typeof resultDisplay === "object" && "type" in resultDisplay && resultDisplay.type === "task_execution") {
|
|
916
|
-
await this.emitTaskUsageFromResultDisplay(
|
|
917
|
-
resultDisplay
|
|
918
|
-
);
|
|
919
|
-
}
|
|
1004
|
+
isExitPlanModeTool(toolName) {
|
|
1005
|
+
return toolName === ToolNames.EXIT_PLAN_MODE;
|
|
920
1006
|
}
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
1007
|
+
/**
|
|
1008
|
+
* Checks if a tool name is the EnterPlanModeTool.
|
|
1009
|
+
*/
|
|
1010
|
+
isEnterPlanModeTool(toolName) {
|
|
1011
|
+
return toolName === ToolNames.ENTER_PLAN_MODE;
|
|
1012
|
+
}
|
|
1013
|
+
/**
|
|
1014
|
+
* Resolves tool metadata from the registry.
|
|
1015
|
+
* Falls back to defaults if tool not found or build fails.
|
|
1016
|
+
*
|
|
1017
|
+
* @param toolName - Name of the tool
|
|
1018
|
+
* @param args - Tool call arguments (used to build invocation)
|
|
1019
|
+
*/
|
|
1020
|
+
resolveToolMetadata(toolName, args) {
|
|
1021
|
+
if (!hasFullSessionContext(this.ctx)) {
|
|
1022
|
+
const description = typeof args?.["description"] === "string" ? args["description"].trim() : "";
|
|
1023
|
+
return {
|
|
1024
|
+
title: description ? `${toolName}: ${description}` : toolName,
|
|
1025
|
+
locations: [],
|
|
1026
|
+
kind: "other"
|
|
1027
|
+
};
|
|
1028
|
+
}
|
|
1029
|
+
const toolRegistry = this.ctx.config.getToolRegistry();
|
|
1030
|
+
const tool = toolRegistry.getTool(toolName);
|
|
1031
|
+
let title = tool?.displayName ?? toolName;
|
|
1032
|
+
let locations = [];
|
|
1033
|
+
let kind = "other";
|
|
1034
|
+
if (tool && args) {
|
|
925
1035
|
try {
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
1036
|
+
const invocation = tool.build(args);
|
|
1037
|
+
title = `${title}: ${invocation.getDescription()}`;
|
|
1038
|
+
locations = invocation.toolLocations().map((loc) => ({
|
|
1039
|
+
path: loc.path,
|
|
1040
|
+
line: loc.line ?? null
|
|
1041
|
+
}));
|
|
1042
|
+
kind = this.mapToolKind(tool.kind, toolName);
|
|
1043
|
+
} catch {
|
|
1044
|
+
if (typeof args["description"] === "string") {
|
|
1045
|
+
title = `${title}: ${args["description"]}`;
|
|
1046
|
+
}
|
|
1047
|
+
if (tool.kind) {
|
|
1048
|
+
kind = this.mapToolKind(tool.kind, toolName);
|
|
1049
|
+
}
|
|
938
1050
|
}
|
|
939
1051
|
}
|
|
940
|
-
|
|
941
|
-
throw firstError;
|
|
942
|
-
}
|
|
1052
|
+
return { title, locations, kind };
|
|
943
1053
|
}
|
|
944
1054
|
/**
|
|
945
|
-
*
|
|
1055
|
+
* Maps core Tool Kind enum to ACP ToolKind string literals.
|
|
1056
|
+
*
|
|
1057
|
+
* @param kind - The core Kind enum value
|
|
1058
|
+
* @param toolName - Optional tool name to handle special cases like exit_plan_mode
|
|
946
1059
|
*/
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
return;
|
|
951
|
-
}
|
|
952
|
-
const usageMetadata = {};
|
|
953
|
-
if (Number.isFinite(summary.inputTokens)) {
|
|
954
|
-
usageMetadata.promptTokenCount = summary.inputTokens;
|
|
955
|
-
}
|
|
956
|
-
if (Number.isFinite(summary.outputTokens)) {
|
|
957
|
-
usageMetadata.candidatesTokenCount = summary.outputTokens;
|
|
958
|
-
}
|
|
959
|
-
if (Number.isFinite(summary.thoughtTokens)) {
|
|
960
|
-
usageMetadata.thoughtsTokenCount = summary.thoughtTokens;
|
|
961
|
-
}
|
|
962
|
-
if (Number.isFinite(summary.cachedTokens)) {
|
|
963
|
-
usageMetadata.cachedContentTokenCount = summary.cachedTokens;
|
|
964
|
-
}
|
|
965
|
-
if (Number.isFinite(summary.totalTokens)) {
|
|
966
|
-
usageMetadata.totalTokenCount = summary.totalTokens;
|
|
967
|
-
}
|
|
968
|
-
if (Object.keys(usageMetadata).length > 0) {
|
|
969
|
-
await this.messageEmitter.emitUsageMetadata(usageMetadata);
|
|
1060
|
+
mapToolKind(kind, toolName) {
|
|
1061
|
+
if (toolName && (this.isExitPlanModeTool(toolName) || this.isEnterPlanModeTool(toolName))) {
|
|
1062
|
+
return "switch_mode";
|
|
970
1063
|
}
|
|
1064
|
+
return KIND_MAP[kind] ?? "other";
|
|
971
1065
|
}
|
|
1066
|
+
// ==================== Private Helpers ====================
|
|
972
1067
|
/**
|
|
973
|
-
*
|
|
974
|
-
*
|
|
975
|
-
* structure (user → agent) on session resume without polluting model context.
|
|
1068
|
+
* Extracts diff content from resultDisplay if it's a diff type (edit tool result).
|
|
1069
|
+
* Returns null if not a diff.
|
|
976
1070
|
*/
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
1071
|
+
extractDiffContent(resultDisplay) {
|
|
1072
|
+
if (!resultDisplay || typeof resultDisplay !== "object") return null;
|
|
1073
|
+
const obj = resultDisplay;
|
|
1074
|
+
if ("fileName" in obj && "newContent" in obj) {
|
|
1075
|
+
if (this.isTruncatedSessionDiffDisplay(resultDisplay)) {
|
|
1076
|
+
return {
|
|
1077
|
+
type: "content",
|
|
1078
|
+
content: {
|
|
1079
|
+
type: "text",
|
|
1080
|
+
text: buildTruncatedDiffPreviewText(obj)
|
|
1081
|
+
}
|
|
1082
|
+
};
|
|
989
1083
|
}
|
|
1084
|
+
return {
|
|
1085
|
+
type: "diff",
|
|
1086
|
+
path: obj["fileName"],
|
|
1087
|
+
oldText: obj["originalContent"] ?? "",
|
|
1088
|
+
newText: obj["newContent"]
|
|
1089
|
+
};
|
|
990
1090
|
}
|
|
1091
|
+
return null;
|
|
1092
|
+
}
|
|
1093
|
+
isTruncatedSessionDiffDisplay(resultDisplay) {
|
|
1094
|
+
if (!resultDisplay || typeof resultDisplay !== "object") return false;
|
|
1095
|
+
const obj = resultDisplay;
|
|
1096
|
+
return obj["truncatedForSession"] === true && "fileName" in obj && "newContent" in obj;
|
|
991
1097
|
}
|
|
992
1098
|
/**
|
|
993
|
-
*
|
|
1099
|
+
* Transforms Part[] to ToolCallContent[].
|
|
1100
|
+
* Extracts text from functionResponse parts and text parts.
|
|
994
1101
|
*/
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1102
|
+
transformPartsToToolCallContent(parts) {
|
|
1103
|
+
const result = [];
|
|
1104
|
+
for (const part of parts) {
|
|
1105
|
+
if ("text" in part && part.text) {
|
|
1106
|
+
result.push({
|
|
1107
|
+
type: "content",
|
|
1108
|
+
content: { type: "text", text: part.text }
|
|
1109
|
+
});
|
|
1110
|
+
}
|
|
1111
|
+
if ("functionResponse" in part && part.functionResponse) {
|
|
1112
|
+
try {
|
|
1113
|
+
const resp = part.functionResponse.response;
|
|
1114
|
+
const outputField = resp["output"];
|
|
1115
|
+
const errorField = resp["error"];
|
|
1116
|
+
const responseText = typeof outputField === "string" ? outputField : typeof errorField === "string" ? errorField : JSON.stringify(resp);
|
|
1117
|
+
result.push({
|
|
1118
|
+
type: "content",
|
|
1119
|
+
content: { type: "text", text: responseText }
|
|
1120
|
+
});
|
|
1121
|
+
} catch {
|
|
1000
1122
|
}
|
|
1001
1123
|
}
|
|
1002
1124
|
}
|
|
1003
|
-
return
|
|
1004
|
-
}
|
|
1005
|
-
setActiveRecordId(recordId, timestamp) {
|
|
1006
|
-
const context = this.ctx;
|
|
1007
|
-
if (typeof context.setActiveRecordId === "function") {
|
|
1008
|
-
context.setActiveRecordId(recordId, timestamp);
|
|
1009
|
-
}
|
|
1125
|
+
return result;
|
|
1010
1126
|
}
|
|
1011
1127
|
};
|
|
1012
1128
|
|
|
1013
|
-
// packages/cli/src/
|
|
1129
|
+
// packages/cli/src/acp-integration/session/history-replayer.ts
|
|
1014
1130
|
init_esbuild_shims();
|
|
1015
|
-
var import_undici2 = __toESM(require_undici(), 1);
|
|
1016
|
-
import { promises as fsp } from "node:fs";
|
|
1017
|
-
import * as path from "node:path";
|
|
1018
1131
|
|
|
1019
|
-
// packages/cli/src/utils/
|
|
1132
|
+
// packages/cli/src/utils/chat-record-tool-call-id.ts
|
|
1020
1133
|
init_esbuild_shims();
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
var debugLogger = createDebugLogger("GIT");
|
|
1024
|
-
async function runGit(args, opts = {}) {
|
|
1025
|
-
return await new Promise((resolve2, reject) => {
|
|
1026
|
-
childProcess.execFile(
|
|
1027
|
-
"git",
|
|
1028
|
-
args,
|
|
1029
|
-
{
|
|
1030
|
-
encoding: "utf-8",
|
|
1031
|
-
...opts.cwd ? { cwd: opts.cwd } : {}
|
|
1032
|
-
},
|
|
1033
|
-
(err, stdout) => {
|
|
1034
|
-
if (err) {
|
|
1035
|
-
reject(err);
|
|
1036
|
-
return;
|
|
1037
|
-
}
|
|
1038
|
-
resolve2(String(stdout ?? "").trim());
|
|
1039
|
-
}
|
|
1040
|
-
);
|
|
1041
|
-
});
|
|
1134
|
+
function getToolResultCallId(record) {
|
|
1135
|
+
return getExplicitToolResultCallId(record) ?? record.uuid;
|
|
1042
1136
|
}
|
|
1043
|
-
__name(
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
return
|
|
1048
|
-
const remoteUrl = line.trim().split(/\s+/)[1];
|
|
1049
|
-
return remoteUrl ? isGitHubRemoteUrl(remoteUrl) : false;
|
|
1050
|
-
});
|
|
1051
|
-
} catch (_error) {
|
|
1052
|
-
debugLogger.debug(`Failed to get git remote:`, _error);
|
|
1053
|
-
return false;
|
|
1054
|
-
}
|
|
1055
|
-
}, "isGitHubRepositoryAsync");
|
|
1056
|
-
function isGitHubRemoteUrl(remoteUrl) {
|
|
1057
|
-
if (remoteUrl.startsWith("git@github.com:")) {
|
|
1058
|
-
return true;
|
|
1059
|
-
}
|
|
1060
|
-
if (remoteUrl.startsWith("git@")) {
|
|
1061
|
-
return false;
|
|
1062
|
-
}
|
|
1063
|
-
try {
|
|
1064
|
-
return new URL(remoteUrl).hostname === "github.com";
|
|
1065
|
-
} catch {
|
|
1066
|
-
return false;
|
|
1137
|
+
__name(getToolResultCallId, "getToolResultCallId");
|
|
1138
|
+
function getExplicitToolResultCallId(record) {
|
|
1139
|
+
const resultCallId = record.toolCallResult?.callId;
|
|
1140
|
+
if (typeof resultCallId === "string" && resultCallId.length > 0) {
|
|
1141
|
+
return resultCallId;
|
|
1067
1142
|
}
|
|
1143
|
+
return extractFunctionResponseId(record);
|
|
1068
1144
|
}
|
|
1069
|
-
__name(
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
throw new Error(`Git repo returned empty value`);
|
|
1145
|
+
__name(getExplicitToolResultCallId, "getExplicitToolResultCallId");
|
|
1146
|
+
function extractFunctionResponseId(record) {
|
|
1147
|
+
if (!record.message?.parts) {
|
|
1148
|
+
return void 0;
|
|
1074
1149
|
}
|
|
1075
|
-
|
|
1076
|
-
|
|
1077
|
-
|
|
1078
|
-
|
|
1079
|
-
const controller = new AbortController();
|
|
1080
|
-
const endpoint = `https://api.github.com/repos/QwenLM/qwen-code-action/releases/latest`;
|
|
1081
|
-
const response = await fetch(endpoint, {
|
|
1082
|
-
method: "GET",
|
|
1083
|
-
headers: {
|
|
1084
|
-
Accept: "application/vnd.github+json",
|
|
1085
|
-
"Content-Type": "application/json",
|
|
1086
|
-
"X-GitHub-Api-Version": "2022-11-28"
|
|
1087
|
-
},
|
|
1088
|
-
dispatcher: proxy ? new import_undici.ProxyAgent(proxy) : void 0,
|
|
1089
|
-
signal: AbortSignal.any([AbortSignal.timeout(3e4), controller.signal])
|
|
1090
|
-
});
|
|
1091
|
-
if (!response.ok) {
|
|
1092
|
-
throw new Error(
|
|
1093
|
-
`Invalid response code: ${response.status} - ${response.statusText}`
|
|
1094
|
-
);
|
|
1095
|
-
}
|
|
1096
|
-
const releaseTag = (await response.json()).tag_name;
|
|
1097
|
-
if (!releaseTag) {
|
|
1098
|
-
throw new Error(`Response did not include tag_name field`);
|
|
1150
|
+
for (const part of record.message.parts) {
|
|
1151
|
+
const id = "functionResponse" in part ? part.functionResponse?.id : void 0;
|
|
1152
|
+
if (typeof id === "string" && id.length > 0) {
|
|
1153
|
+
return id;
|
|
1099
1154
|
}
|
|
1100
|
-
return releaseTag;
|
|
1101
|
-
} catch (_error) {
|
|
1102
|
-
debugLogger.debug(
|
|
1103
|
-
`Failed to determine latest qwen-code-action release:`,
|
|
1104
|
-
_error
|
|
1105
|
-
);
|
|
1106
|
-
throw new Error(
|
|
1107
|
-
`Unable to determine the latest qwen-code-action release on GitHub.`
|
|
1108
|
-
);
|
|
1109
|
-
}
|
|
1110
|
-
}, "getLatestGitHubRelease");
|
|
1111
|
-
async function getGitHubRepoInfoAsync(opts = {}) {
|
|
1112
|
-
return parseGitHubRepoInfo(
|
|
1113
|
-
await runGit(["remote", "get-url", "origin"], opts)
|
|
1114
|
-
);
|
|
1115
|
-
}
|
|
1116
|
-
__name(getGitHubRepoInfoAsync, "getGitHubRepoInfoAsync");
|
|
1117
|
-
function parseGitHubRepoInfo(remoteUrl) {
|
|
1118
|
-
let urlToParse = remoteUrl;
|
|
1119
|
-
if (remoteUrl.startsWith("git@github.com:")) {
|
|
1120
|
-
urlToParse = remoteUrl.replace("git@github.com:", "");
|
|
1121
|
-
} else if (remoteUrl.startsWith("git@")) {
|
|
1122
|
-
throw new Error(
|
|
1123
|
-
`Owner & repo could not be extracted from remote URL: ${remoteUrl}`
|
|
1124
|
-
);
|
|
1125
|
-
}
|
|
1126
|
-
let parsedUrl;
|
|
1127
|
-
try {
|
|
1128
|
-
parsedUrl = new URL(urlToParse, "https://github.com");
|
|
1129
|
-
} catch {
|
|
1130
|
-
throw new Error(
|
|
1131
|
-
`Owner & repo could not be extracted from remote URL: ${remoteUrl}`
|
|
1132
|
-
);
|
|
1133
|
-
}
|
|
1134
|
-
if (parsedUrl.hostname !== "github.com") {
|
|
1135
|
-
throw new Error(
|
|
1136
|
-
`Owner & repo could not be extracted from remote URL: ${remoteUrl}`
|
|
1137
|
-
);
|
|
1138
|
-
}
|
|
1139
|
-
const parts = parsedUrl.pathname.split("/").filter((part) => part !== "");
|
|
1140
|
-
if (parts.length !== 2 || !parts[0] || !parts[1]) {
|
|
1141
|
-
throw new Error(
|
|
1142
|
-
`Owner & repo could not be extracted from remote URL: ${remoteUrl}`
|
|
1143
|
-
);
|
|
1144
1155
|
}
|
|
1145
|
-
return
|
|
1156
|
+
return void 0;
|
|
1146
1157
|
}
|
|
1147
|
-
__name(
|
|
1158
|
+
__name(extractFunctionResponseId, "extractFunctionResponseId");
|
|
1148
1159
|
|
|
1149
|
-
// packages/cli/src/
|
|
1150
|
-
var
|
|
1151
|
-
var
|
|
1152
|
-
"qwen-dispatch/qwen-dispatch.yml",
|
|
1153
|
-
"qwen-assistant/qwen-invoke.yml",
|
|
1154
|
-
"issue-triage/qwen-triage.yml",
|
|
1155
|
-
"issue-triage/qwen-scheduled-triage.yml",
|
|
1156
|
-
"pr-review/qwen-review.yml"
|
|
1157
|
-
];
|
|
1158
|
-
var GITIGNORE_ENTRIES = [".qwen/", "gha-creds-*.json"];
|
|
1159
|
-
var MAX_WORKFLOW_DOWNLOAD_BYTES = 5 * 1024 * 1024;
|
|
1160
|
-
var SetupGithubError = class extends Error {
|
|
1160
|
+
// packages/cli/src/acp-integration/session/history-replayer.ts
|
|
1161
|
+
var MISSING_TOOL_RESULT_MESSAGE = "Tool result missing from saved history; the previous run likely ended before this tool completed.";
|
|
1162
|
+
var HistoryReplayer = class {
|
|
1161
1163
|
static {
|
|
1162
|
-
__name(this, "
|
|
1164
|
+
__name(this, "HistoryReplayer");
|
|
1163
1165
|
}
|
|
1164
|
-
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
|
|
1168
|
-
constructor(
|
|
1169
|
-
|
|
1170
|
-
this.
|
|
1171
|
-
this.
|
|
1172
|
-
this.status = status;
|
|
1173
|
-
this.partial = partialResult !== void 0;
|
|
1174
|
-
this.partialResult = partialResult;
|
|
1166
|
+
ctx;
|
|
1167
|
+
messageEmitter;
|
|
1168
|
+
toolCallEmitter;
|
|
1169
|
+
pendingReplayToolCalls = /* @__PURE__ */ new Map();
|
|
1170
|
+
constructor(ctx) {
|
|
1171
|
+
this.ctx = ctx;
|
|
1172
|
+
this.messageEmitter = new MessageEmitter(ctx);
|
|
1173
|
+
this.toolCallEmitter = new ToolCallEmitter(ctx);
|
|
1175
1174
|
}
|
|
1176
|
-
|
|
1177
|
-
|
|
1178
|
-
|
|
1179
|
-
|
|
1180
|
-
|
|
1181
|
-
|
|
1182
|
-
|
|
1183
|
-
|
|
1184
|
-
|
|
1185
|
-
|
|
1186
|
-
|
|
1187
|
-
|
|
1188
|
-
|
|
1189
|
-
|
|
1190
|
-
|
|
1175
|
+
/**
|
|
1176
|
+
* Replays all chat records from a loaded session.
|
|
1177
|
+
*
|
|
1178
|
+
* @param records - Array of chat records to replay
|
|
1179
|
+
* @param gaps - Optional detected history gaps; a visible notice is emitted
|
|
1180
|
+
* immediately before each gap's child record so the user sees that an
|
|
1181
|
+
* earlier segment was lost rather than assuming the halves are contiguous.
|
|
1182
|
+
*/
|
|
1183
|
+
async replay(records, gaps) {
|
|
1184
|
+
try {
|
|
1185
|
+
await this.replayPage(records, { finalizeDangling: true, gaps });
|
|
1186
|
+
} finally {
|
|
1187
|
+
this.pendingReplayToolCalls.clear();
|
|
1188
|
+
this.setActiveRecordId(null);
|
|
1189
|
+
}
|
|
1190
|
+
}
|
|
1191
|
+
async replayPage(records, options = {}) {
|
|
1192
|
+
this.pendingReplayToolCalls.clear();
|
|
1193
|
+
for (const pending of options.pendingToolCalls ?? []) {
|
|
1194
|
+
this.pendingReplayToolCalls.set(pending.callId, pending);
|
|
1195
|
+
}
|
|
1196
|
+
const gapByChildUuid = indexGapsByChild(options.gaps);
|
|
1197
|
+
let replayError;
|
|
1191
1198
|
try {
|
|
1192
|
-
|
|
1199
|
+
for (const record of records) {
|
|
1200
|
+
const gap = gapByChildUuid.get(record.uuid);
|
|
1201
|
+
if (gap) {
|
|
1202
|
+
await this.emitHistoryGapNotice(gap, record.timestamp);
|
|
1203
|
+
}
|
|
1204
|
+
await this.replayRecord(record);
|
|
1205
|
+
}
|
|
1193
1206
|
} catch (error) {
|
|
1194
|
-
|
|
1195
|
-
|
|
1207
|
+
replayError = error;
|
|
1208
|
+
}
|
|
1209
|
+
let danglingError;
|
|
1210
|
+
if (options.finalizeDangling === true) {
|
|
1211
|
+
try {
|
|
1212
|
+
await this.failDanglingToolCalls();
|
|
1213
|
+
} catch (error) {
|
|
1214
|
+
danglingError = error;
|
|
1196
1215
|
}
|
|
1197
|
-
throw error;
|
|
1198
1216
|
}
|
|
1217
|
+
const state = {
|
|
1218
|
+
pendingToolCalls: options.finalizeDangling === true ? [] : Array.from(this.pendingReplayToolCalls.values())
|
|
1219
|
+
};
|
|
1220
|
+
this.setActiveRecordId(null);
|
|
1221
|
+
if (replayError && danglingError) {
|
|
1222
|
+
throw new AggregateError(
|
|
1223
|
+
[replayError, danglingError],
|
|
1224
|
+
"Replay and dangling-cleanup both failed"
|
|
1225
|
+
);
|
|
1226
|
+
}
|
|
1227
|
+
if (replayError) {
|
|
1228
|
+
throw replayError;
|
|
1229
|
+
}
|
|
1230
|
+
if (danglingError) {
|
|
1231
|
+
throw danglingError;
|
|
1232
|
+
}
|
|
1233
|
+
return state;
|
|
1199
1234
|
}
|
|
1200
|
-
|
|
1201
|
-
|
|
1202
|
-
const cwd = options.cwd ?? process.cwd();
|
|
1203
|
-
const fileOps = options.fileOps ?? nodeFileOps;
|
|
1204
|
-
if (!await isGitHubRepositoryAsync({ cwd })) {
|
|
1205
|
-
throw new SetupGithubError(
|
|
1206
|
-
"github_repository_not_found",
|
|
1207
|
-
"Unable to determine the GitHub repository. /setup-github must be run from a git repository.",
|
|
1208
|
-
400
|
|
1209
|
-
);
|
|
1210
|
-
}
|
|
1211
|
-
let gitRepoRoot;
|
|
1212
|
-
try {
|
|
1213
|
-
gitRepoRoot = await getGitRepoRootAsync({ cwd });
|
|
1214
|
-
} catch (error) {
|
|
1215
|
-
debugLogger2.debug("Failed to get git repo root:", error);
|
|
1216
|
-
throw new SetupGithubError(
|
|
1217
|
-
"github_repository_not_found",
|
|
1218
|
-
"Unable to determine the GitHub repository. /setup-github must be run from a git repository.",
|
|
1219
|
-
400
|
|
1220
|
-
);
|
|
1235
|
+
getPendingToolCalls() {
|
|
1236
|
+
return Array.from(this.pendingReplayToolCalls.values());
|
|
1221
1237
|
}
|
|
1222
|
-
|
|
1223
|
-
|
|
1224
|
-
|
|
1225
|
-
|
|
1226
|
-
|
|
1227
|
-
|
|
1228
|
-
|
|
1229
|
-
"
|
|
1230
|
-
|
|
1231
|
-
|
|
1232
|
-
|
|
1238
|
+
/**
|
|
1239
|
+
* Replays a single chat record.
|
|
1240
|
+
*/
|
|
1241
|
+
async replayRecord(record) {
|
|
1242
|
+
this.setActiveRecordId(record.uuid, record.timestamp);
|
|
1243
|
+
try {
|
|
1244
|
+
switch (record.type) {
|
|
1245
|
+
case "user":
|
|
1246
|
+
if (record.subtype === "notification" || record.subtype === "cron") {
|
|
1247
|
+
const displayText = record.systemPayload?.displayText;
|
|
1248
|
+
if (displayText) {
|
|
1249
|
+
await this.messageEmitter.emitUserMessage(
|
|
1250
|
+
displayText,
|
|
1251
|
+
record.timestamp,
|
|
1252
|
+
record.subtype === "cron" ? { source: "cron" } : void 0
|
|
1253
|
+
);
|
|
1254
|
+
}
|
|
1255
|
+
break;
|
|
1256
|
+
}
|
|
1257
|
+
if (record.subtype === "mid_turn_user_message") {
|
|
1258
|
+
const displayText = record.systemPayload?.displayText;
|
|
1259
|
+
if (displayText) {
|
|
1260
|
+
await this.messageEmitter.emitUserMessage(
|
|
1261
|
+
displayText,
|
|
1262
|
+
record.timestamp
|
|
1263
|
+
);
|
|
1264
|
+
} else if (record.message) {
|
|
1265
|
+
await this.replayContent(
|
|
1266
|
+
record.message,
|
|
1267
|
+
"user",
|
|
1268
|
+
record.timestamp,
|
|
1269
|
+
record.uuid
|
|
1270
|
+
);
|
|
1271
|
+
}
|
|
1272
|
+
break;
|
|
1273
|
+
}
|
|
1274
|
+
if (record.message) {
|
|
1275
|
+
await this.replayContent(
|
|
1276
|
+
record.message,
|
|
1277
|
+
"user",
|
|
1278
|
+
record.timestamp,
|
|
1279
|
+
record.uuid
|
|
1280
|
+
);
|
|
1281
|
+
}
|
|
1282
|
+
break;
|
|
1283
|
+
case "assistant":
|
|
1284
|
+
if (record.message) {
|
|
1285
|
+
await this.replayContent(
|
|
1286
|
+
record.message,
|
|
1287
|
+
"assistant",
|
|
1288
|
+
record.timestamp,
|
|
1289
|
+
record.uuid
|
|
1290
|
+
);
|
|
1291
|
+
}
|
|
1292
|
+
if (record.usageMetadata) {
|
|
1293
|
+
await this.replayUsageMetadata(record.usageMetadata);
|
|
1294
|
+
}
|
|
1295
|
+
break;
|
|
1296
|
+
case "tool_result":
|
|
1297
|
+
await this.replayToolResult(record);
|
|
1298
|
+
break;
|
|
1299
|
+
case "system":
|
|
1300
|
+
if (record.subtype === "slash_command") {
|
|
1301
|
+
await this.replaySlashCommandResult(record);
|
|
1302
|
+
}
|
|
1303
|
+
break;
|
|
1304
|
+
default:
|
|
1305
|
+
break;
|
|
1306
|
+
}
|
|
1307
|
+
} finally {
|
|
1308
|
+
this.setActiveRecordId(null);
|
|
1233
1309
|
}
|
|
1234
1310
|
}
|
|
1235
|
-
|
|
1236
|
-
|
|
1237
|
-
|
|
1238
|
-
|
|
1239
|
-
|
|
1240
|
-
|
|
1241
|
-
|
|
1242
|
-
|
|
1243
|
-
|
|
1244
|
-
|
|
1245
|
-
|
|
1246
|
-
|
|
1247
|
-
throw new SetupGithubError(
|
|
1248
|
-
"github_release_lookup_failed",
|
|
1249
|
-
"Unable to determine the latest qwen-code-action release on GitHub.",
|
|
1250
|
-
502
|
|
1311
|
+
/**
|
|
1312
|
+
* Emits a visible notice marking a break in the persisted history chain: an
|
|
1313
|
+
* earlier segment was physically lost (storage interruption) and could not be
|
|
1314
|
+
* recovered, so the surviving turns below must not be read as contiguous with
|
|
1315
|
+
* whatever came before the gap. Uses the agent message channel — the same one
|
|
1316
|
+
* used for other system notices (see MessageEmitter.emitStopHookLoop) — so no
|
|
1317
|
+
* new session-update kind is needed.
|
|
1318
|
+
*/
|
|
1319
|
+
async emitHistoryGapNotice(gap, timestamp) {
|
|
1320
|
+
await this.messageEmitter.emitAgentMessage(
|
|
1321
|
+
formatHistoryGapNotice(gap),
|
|
1322
|
+
timestamp
|
|
1251
1323
|
);
|
|
1252
1324
|
}
|
|
1253
|
-
|
|
1254
|
-
|
|
1255
|
-
|
|
1256
|
-
|
|
1257
|
-
|
|
1258
|
-
|
|
1259
|
-
|
|
1260
|
-
|
|
1261
|
-
|
|
1262
|
-
|
|
1263
|
-
|
|
1264
|
-
|
|
1265
|
-
|
|
1266
|
-
|
|
1267
|
-
|
|
1268
|
-
|
|
1269
|
-
|
|
1270
|
-
|
|
1271
|
-
|
|
1272
|
-
|
|
1273
|
-
|
|
1274
|
-
|
|
1325
|
+
/**
|
|
1326
|
+
* Replays content from a message (user or assistant).
|
|
1327
|
+
* Handles text parts, thought parts, and function calls.
|
|
1328
|
+
*
|
|
1329
|
+
* @param content - The content to replay
|
|
1330
|
+
* @param role - The role (user or assistant)
|
|
1331
|
+
* @param timestamp - Optional server-side timestamp from the JSONL record
|
|
1332
|
+
*/
|
|
1333
|
+
async replayContent(content, role, timestamp, recordId) {
|
|
1334
|
+
for (const part of content.parts ?? []) {
|
|
1335
|
+
if ("text" in part && part.text) {
|
|
1336
|
+
const isThought = part.thought ?? false;
|
|
1337
|
+
await this.messageEmitter.emitMessage(
|
|
1338
|
+
part.text,
|
|
1339
|
+
role,
|
|
1340
|
+
isThought,
|
|
1341
|
+
timestamp
|
|
1342
|
+
);
|
|
1343
|
+
}
|
|
1344
|
+
if ("functionCall" in part && part.functionCall) {
|
|
1345
|
+
const functionName = part.functionCall.name ?? "";
|
|
1346
|
+
const sourceCallId = part.functionCall.id;
|
|
1347
|
+
const callId = sourceCallId ?? `${functionName}-${Date.now()}`;
|
|
1348
|
+
const emitted = await this.toolCallEmitter.emitStart({
|
|
1349
|
+
toolName: functionName,
|
|
1350
|
+
callId,
|
|
1351
|
+
args: part.functionCall.args,
|
|
1352
|
+
status: "in_progress",
|
|
1353
|
+
timestamp
|
|
1354
|
+
});
|
|
1355
|
+
if (emitted && role === "assistant" && recordId && sourceCallId) {
|
|
1356
|
+
this.pendingReplayToolCalls.set(callId, {
|
|
1357
|
+
callId,
|
|
1358
|
+
toolName: functionName,
|
|
1359
|
+
timestamp,
|
|
1360
|
+
recordId
|
|
1361
|
+
});
|
|
1362
|
+
}
|
|
1363
|
+
}
|
|
1364
|
+
}
|
|
1275
1365
|
}
|
|
1276
|
-
|
|
1277
|
-
|
|
1278
|
-
|
|
1279
|
-
|
|
1280
|
-
|
|
1281
|
-
|
|
1282
|
-
|
|
1366
|
+
/**
|
|
1367
|
+
* Replays usage metadata.
|
|
1368
|
+
* @param usageMetadata - The usage metadata to replay
|
|
1369
|
+
*/
|
|
1370
|
+
async replayUsageMetadata(usageMetadata) {
|
|
1371
|
+
await this.messageEmitter.emitUsageMetadata(usageMetadata);
|
|
1372
|
+
}
|
|
1373
|
+
/**
|
|
1374
|
+
* Replays a tool result record.
|
|
1375
|
+
*/
|
|
1376
|
+
async replayToolResult(record) {
|
|
1377
|
+
if (!record.message?.parts) {
|
|
1378
|
+
return;
|
|
1379
|
+
}
|
|
1380
|
+
const result = record.toolCallResult;
|
|
1381
|
+
const callId = getToolResultCallId(record);
|
|
1382
|
+
this.pendingReplayToolCalls.delete(callId);
|
|
1383
|
+
const toolName = this.extractToolNameFromRecord(record);
|
|
1384
|
+
await this.toolCallEmitter.emitResult({
|
|
1385
|
+
toolName,
|
|
1386
|
+
callId,
|
|
1387
|
+
success: !result?.error,
|
|
1388
|
+
message: record.message.parts,
|
|
1389
|
+
resultDisplay: result?.resultDisplay,
|
|
1390
|
+
artifacts: result?.artifacts,
|
|
1391
|
+
// For TodoWriteTool fallback, try to extract args from the record
|
|
1392
|
+
// Note: args aren't stored in tool_result records by default
|
|
1393
|
+
args: void 0,
|
|
1394
|
+
timestamp: record.timestamp
|
|
1395
|
+
});
|
|
1396
|
+
const { resultDisplay } = result ?? {};
|
|
1397
|
+
if (!!resultDisplay && typeof resultDisplay === "object" && "type" in resultDisplay && resultDisplay.type === "task_execution") {
|
|
1398
|
+
await this.emitTaskUsageFromResultDisplay(
|
|
1399
|
+
resultDisplay
|
|
1283
1400
|
);
|
|
1401
|
+
}
|
|
1402
|
+
}
|
|
1403
|
+
async failDanglingToolCalls() {
|
|
1404
|
+
let firstError;
|
|
1405
|
+
for (const pending of this.pendingReplayToolCalls.values()) {
|
|
1406
|
+
this.setActiveRecordId(pending.recordId, pending.timestamp);
|
|
1284
1407
|
try {
|
|
1285
|
-
|
|
1286
|
-
|
|
1287
|
-
|
|
1288
|
-
|
|
1289
|
-
|
|
1290
|
-
|
|
1291
|
-
|
|
1292
|
-
path: relativePath,
|
|
1293
|
-
status: "written",
|
|
1294
|
-
sizeBytes: write.sizeBytes
|
|
1408
|
+
await this.toolCallEmitter.emitResult({
|
|
1409
|
+
toolName: pending.toolName,
|
|
1410
|
+
callId: pending.callId,
|
|
1411
|
+
success: false,
|
|
1412
|
+
message: [],
|
|
1413
|
+
error: new Error(MISSING_TOOL_RESULT_MESSAGE),
|
|
1414
|
+
timestamp: pending.timestamp
|
|
1295
1415
|
});
|
|
1296
1416
|
} catch (error) {
|
|
1297
|
-
|
|
1298
|
-
|
|
1299
|
-
|
|
1300
|
-
path: relativePath,
|
|
1301
|
-
status: "failed",
|
|
1302
|
-
error: error instanceof Error ? error.message : String(error)
|
|
1303
|
-
});
|
|
1304
|
-
throw new SetupGithubError(
|
|
1305
|
-
"github_workflow_write_failed",
|
|
1306
|
-
`Unable to write ${relativePath}.`,
|
|
1307
|
-
500,
|
|
1308
|
-
result
|
|
1309
|
-
);
|
|
1417
|
+
firstError ??= error;
|
|
1418
|
+
} finally {
|
|
1419
|
+
this.setActiveRecordId(null);
|
|
1310
1420
|
}
|
|
1311
1421
|
}
|
|
1312
|
-
|
|
1313
|
-
|
|
1314
|
-
|
|
1315
|
-
"github_workflow_write_failed",
|
|
1316
|
-
"Unable to create .github/workflows.",
|
|
1317
|
-
500,
|
|
1318
|
-
result
|
|
1319
|
-
);
|
|
1422
|
+
if (firstError) {
|
|
1423
|
+
throw firstError;
|
|
1424
|
+
}
|
|
1320
1425
|
}
|
|
1321
|
-
|
|
1322
|
-
|
|
1323
|
-
|
|
1324
|
-
async
|
|
1325
|
-
|
|
1326
|
-
|
|
1327
|
-
|
|
1328
|
-
".gitignore"
|
|
1329
|
-
);
|
|
1330
|
-
if (existingContent === void 0) {
|
|
1331
|
-
const content = GITIGNORE_ENTRIES.join("\n") + "\n";
|
|
1332
|
-
await fileOps.writeTextFile(gitRepoRoot, ".gitignore", content);
|
|
1333
|
-
return {
|
|
1334
|
-
path: ".gitignore",
|
|
1335
|
-
status: "created",
|
|
1336
|
-
added: [...GITIGNORE_ENTRIES]
|
|
1337
|
-
};
|
|
1426
|
+
/**
|
|
1427
|
+
* Emits token usage from a AgentResultDisplay execution summary, if present.
|
|
1428
|
+
*/
|
|
1429
|
+
async emitTaskUsageFromResultDisplay(resultDisplay) {
|
|
1430
|
+
const summary = resultDisplay.executionSummary;
|
|
1431
|
+
if (!summary) {
|
|
1432
|
+
return;
|
|
1338
1433
|
}
|
|
1339
|
-
const
|
|
1340
|
-
|
|
1341
|
-
|
|
1342
|
-
if (missingEntries.length === 0) {
|
|
1343
|
-
return { path: ".gitignore", status: "unchanged" };
|
|
1434
|
+
const usageMetadata = {};
|
|
1435
|
+
if (Number.isFinite(summary.inputTokens)) {
|
|
1436
|
+
usageMetadata.promptTokenCount = summary.inputTokens;
|
|
1344
1437
|
}
|
|
1345
|
-
|
|
1346
|
-
|
|
1347
|
-
|
|
1348
|
-
|
|
1349
|
-
|
|
1350
|
-
|
|
1351
|
-
|
|
1352
|
-
|
|
1353
|
-
|
|
1354
|
-
|
|
1355
|
-
|
|
1356
|
-
|
|
1357
|
-
|
|
1358
|
-
|
|
1359
|
-
}
|
|
1360
|
-
}
|
|
1361
|
-
__name(updateGitignore, "updateGitignore");
|
|
1362
|
-
async function downloadWorkflows(options) {
|
|
1363
|
-
const internalAbort = new AbortController();
|
|
1364
|
-
try {
|
|
1365
|
-
const dispatcher = options.proxy ? new import_undici2.ProxyAgent(options.proxy) : void 0;
|
|
1366
|
-
return await Promise.all(
|
|
1367
|
-
GITHUB_WORKFLOW_PATHS.map(async (workflow) => {
|
|
1368
|
-
const endpoint = `https://raw.githubusercontent.com/QwenLM/qwen-code-action/refs/tags/${options.releaseTag}/examples/workflows/${workflow}`;
|
|
1369
|
-
const response = await options.fetchImpl(endpoint, {
|
|
1370
|
-
method: "GET",
|
|
1371
|
-
dispatcher,
|
|
1372
|
-
signal: AbortSignal.any([
|
|
1373
|
-
AbortSignal.timeout(3e4),
|
|
1374
|
-
internalAbort.signal,
|
|
1375
|
-
...options.abortSignal ? [options.abortSignal] : []
|
|
1376
|
-
])
|
|
1377
|
-
});
|
|
1378
|
-
if (!response.ok) {
|
|
1379
|
-
throw new Error(
|
|
1380
|
-
`Invalid response code downloading ${endpoint}: ${response.status} - ${response.statusText}`
|
|
1381
|
-
);
|
|
1382
|
-
}
|
|
1383
|
-
return {
|
|
1384
|
-
sourcePath: workflow,
|
|
1385
|
-
content: await readResponseTextWithLimit(response, workflow)
|
|
1386
|
-
};
|
|
1387
|
-
})
|
|
1388
|
-
);
|
|
1389
|
-
} catch (error) {
|
|
1390
|
-
internalAbort.abort();
|
|
1391
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
1392
|
-
debugLogger2.debug("Failed to download qwen-code-action workflows:", error);
|
|
1393
|
-
throw new SetupGithubError(
|
|
1394
|
-
"github_workflow_download_failed",
|
|
1395
|
-
`Unable to download qwen-code-action workflows from GitHub. ${message}`,
|
|
1396
|
-
502
|
|
1397
|
-
);
|
|
1398
|
-
}
|
|
1399
|
-
}
|
|
1400
|
-
__name(downloadWorkflows, "downloadWorkflows");
|
|
1401
|
-
async function readResponseTextWithLimit(response, sourcePath) {
|
|
1402
|
-
const contentLength = response.headers.get("content-length");
|
|
1403
|
-
if (contentLength !== null) {
|
|
1404
|
-
const parsedLength = Number(contentLength);
|
|
1405
|
-
if (Number.isFinite(parsedLength) && parsedLength > MAX_WORKFLOW_DOWNLOAD_BYTES) {
|
|
1406
|
-
throw new Error(
|
|
1407
|
-
`${sourcePath} exceeds download limit of ${MAX_WORKFLOW_DOWNLOAD_BYTES} bytes`
|
|
1408
|
-
);
|
|
1438
|
+
if (Number.isFinite(summary.outputTokens)) {
|
|
1439
|
+
usageMetadata.candidatesTokenCount = summary.outputTokens;
|
|
1440
|
+
}
|
|
1441
|
+
if (Number.isFinite(summary.thoughtTokens)) {
|
|
1442
|
+
usageMetadata.thoughtsTokenCount = summary.thoughtTokens;
|
|
1443
|
+
}
|
|
1444
|
+
if (Number.isFinite(summary.cachedTokens)) {
|
|
1445
|
+
usageMetadata.cachedContentTokenCount = summary.cachedTokens;
|
|
1446
|
+
}
|
|
1447
|
+
if (Number.isFinite(summary.totalTokens)) {
|
|
1448
|
+
usageMetadata.totalTokenCount = summary.totalTokens;
|
|
1449
|
+
}
|
|
1450
|
+
if (Object.keys(usageMetadata).length > 0) {
|
|
1451
|
+
await this.messageEmitter.emitUsageMetadata(usageMetadata);
|
|
1409
1452
|
}
|
|
1410
1453
|
}
|
|
1411
|
-
|
|
1412
|
-
|
|
1413
|
-
|
|
1414
|
-
|
|
1415
|
-
|
|
1416
|
-
|
|
1417
|
-
|
|
1418
|
-
|
|
1419
|
-
|
|
1420
|
-
|
|
1421
|
-
|
|
1422
|
-
|
|
1423
|
-
|
|
1424
|
-
|
|
1425
|
-
|
|
1454
|
+
/**
|
|
1455
|
+
* Replays a slash_command system record by re-emitting its output as an
|
|
1456
|
+
* agent message chunk. This allows Zed to reconstruct the correct turn
|
|
1457
|
+
* structure (user → agent) on session resume without polluting model context.
|
|
1458
|
+
*/
|
|
1459
|
+
async replaySlashCommandResult(record) {
|
|
1460
|
+
const payload = record.systemPayload;
|
|
1461
|
+
if (payload?.phase !== "result" || !payload.outputHistoryItems?.length) {
|
|
1462
|
+
return;
|
|
1463
|
+
}
|
|
1464
|
+
for (const item of payload.outputHistoryItems) {
|
|
1465
|
+
const text = typeof item["text"] === "string" ? item["text"] : "";
|
|
1466
|
+
if (text) {
|
|
1467
|
+
await this.messageEmitter.emitSlashCommandOutput(
|
|
1468
|
+
text.replace(/\n/g, " \n"),
|
|
1469
|
+
record.timestamp
|
|
1426
1470
|
);
|
|
1427
1471
|
}
|
|
1428
|
-
chunks.push(value);
|
|
1429
1472
|
}
|
|
1430
|
-
} finally {
|
|
1431
|
-
reader.releaseLock();
|
|
1432
|
-
}
|
|
1433
|
-
const body = new Uint8Array(totalBytes);
|
|
1434
|
-
let offset = 0;
|
|
1435
|
-
for (const chunk of chunks) {
|
|
1436
|
-
body.set(chunk, offset);
|
|
1437
|
-
offset += chunk.byteLength;
|
|
1438
1473
|
}
|
|
1439
|
-
|
|
1440
|
-
|
|
1441
|
-
|
|
1442
|
-
|
|
1443
|
-
|
|
1444
|
-
|
|
1445
|
-
|
|
1446
|
-
|
|
1447
|
-
|
|
1474
|
+
/**
|
|
1475
|
+
* Extracts tool name from a chat record's function response.
|
|
1476
|
+
*/
|
|
1477
|
+
extractToolNameFromRecord(record) {
|
|
1478
|
+
if (record.message?.parts) {
|
|
1479
|
+
for (const part of record.message.parts) {
|
|
1480
|
+
if ("functionResponse" in part && part.functionResponse?.name) {
|
|
1481
|
+
return part.functionResponse.name;
|
|
1482
|
+
}
|
|
1483
|
+
}
|
|
1484
|
+
}
|
|
1485
|
+
return "";
|
|
1448
1486
|
}
|
|
1449
|
-
|
|
1450
|
-
|
|
1451
|
-
|
|
1452
|
-
|
|
1453
|
-
|
|
1454
|
-
} catch {
|
|
1455
|
-
return path.resolve(input);
|
|
1487
|
+
setActiveRecordId(recordId, timestamp) {
|
|
1488
|
+
const context = this.ctx;
|
|
1489
|
+
if (typeof context.setActiveRecordId === "function") {
|
|
1490
|
+
context.setActiveRecordId(recordId, timestamp);
|
|
1491
|
+
}
|
|
1456
1492
|
}
|
|
1457
|
-
}
|
|
1458
|
-
__name(realpathOrResolve, "realpathOrResolve");
|
|
1493
|
+
};
|
|
1459
1494
|
|
|
1460
1495
|
// packages/cli/src/ui/utils/export/collect.ts
|
|
1461
1496
|
init_esbuild_shims();
|
|
@@ -1646,7 +1681,7 @@ async function extractMetadata(conversation, config) {
|
|
|
1646
1681
|
const gitBranch = firstRecord?.gitBranch;
|
|
1647
1682
|
let gitRepo;
|
|
1648
1683
|
if (cwd) {
|
|
1649
|
-
const { getGitRepoName } = await import("./src-
|
|
1684
|
+
const { getGitRepoName } = await import("./src-DWZT2X3Z.js");
|
|
1650
1685
|
gitRepo = getGitRepoName(cwd);
|
|
1651
1686
|
}
|
|
1652
1687
|
let model;
|
|
@@ -2491,7 +2526,7 @@ export {
|
|
|
2491
2526
|
};
|
|
2492
2527
|
/**
|
|
2493
2528
|
* @license
|
|
2494
|
-
* Copyright 2025
|
|
2529
|
+
* Copyright 2025 Google LLC
|
|
2495
2530
|
* SPDX-License-Identifier: Apache-2.0
|
|
2496
2531
|
*/
|
|
2497
2532
|
/**
|
|
@@ -2501,7 +2536,7 @@ export {
|
|
|
2501
2536
|
*/
|
|
2502
2537
|
/**
|
|
2503
2538
|
* @license
|
|
2504
|
-
* Copyright 2025
|
|
2539
|
+
* Copyright 2025 Qwen
|
|
2505
2540
|
* SPDX-License-Identifier: Apache-2.0
|
|
2506
2541
|
*/
|
|
2507
2542
|
/**
|